Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Sérgio Martins,
Nicolas Arnaud-Cormos
9 November 2023
In a previous blog we demonstrated the most straightforward method to optimize Visual Studio Code for a Qt / C++ environment: simply let the tools do all the work! The example GitHub project we discussed automatically installs both the Microsoft C/C++ and clangd extensions into VS Code. You might wonder why you need both C++ extensions. The short answer is that having both will maximize your productivity in every situation… but read on for more detail.
The thing that makes VS Code an amazing productivity tool is that you can add all kinds of extensions to it – tools to help you write, document, and debug code. While the C/C++ extensions from Microsoft and clangd do many of the same things, they each have individual strong suits.
A rather simple block diagram of our dual C++ extensions looks something like this.
VS Code with both the C/C++ and clangd extensions installed.
Extensions that use the language server protocol (LSP) can add feature support tailored to the programming language in use. LSP allows a “mere” editor application like VS Code to provide features like auto completion, identifier location, grammar-sensitive tips, parameter name hints, and refactoring operations, and both the Microsoft C/C++ extension and clangd use LSP.
However, you can also see that the Microsoft C/C++ extension uses a debug adaptor to connect to different compiler toolchains. This provides debugging functionality for all the debuggers that the C/C++ extension supports. That means that if you want to debug C++ code from within VS Code, you need the Microsoft C/C++ extension installed (or an equivalent C++ extension that has a debug adapter – something that clangd notably does not have).
But if the Microsoft C/C++ extension provides both language support and debugging support, why do you need clangd at all?
There are a few small differences between the two in the refactoring or code actions that they offer, but the biggest difference is in speed.
For finding identifiers and taking you to their definition or their references, clangd is significantly faster than Microsoft. What is almost instantaneous with clangd takes a second or two with the Microsoft C/C++ extension, requiring a progress bar. Most developers spend more time reading code than writing it, and I almost always prefer enabling clangd because of this. Especially for developers who are frequently navigating big source trees, those little delays can be a minor annoyance that adds up.
Thankfully, you can have both extensions enabled, but disable the Microsoft C/C++ Intellisense features. That lets you use the faster clangd source navigation and still use the Microsoft extension when it comes to debugging your code.
When installing the clangd extension, it will automatically disable the Microsoft C/C++ Intellisense features for you. If it's not the case, you can change this setting:
"C_Cpp.intelliSenseEngine": "disabled",
You do need a couple settings to make clangd work properly. These are automatically configured in our VS Code C++/Qt template but to do it by hand you’ll need the following:
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
near the top of your CMakeLists.txt file or set the CMAKE_EXPORT_COMPILE_COMMANDS
environment variable to 1 in the environment where you will run CMake."cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json",
to the .vscode/settings.json file in your project directory.
These two commands ensure cmake will capture the compiler commands that it needs to build the project during the configure stage. The file containing those commands is then copied to the root of the workspace where the clangd extension expects them. Without these two pieces, clangd won’t work properly.
You don’t need to do a full build to create and install your compile_commands.json file, you can just select the appropriate cmake preset on the bottom status bar. Alternatively, you can type Ctrl+Shift+P (Command+Shift+P on a Mac) to get the command palette and type “CMake: Configure”.
Everything said up to now is more than enough for most cases, but there are still some open issues we may need to take care of:
compile_commands.json
file. What happens if we are working in a multi-folder workspace, where each folder is an independently buildable application or library?We can get to a solution for both cases by using clang configuration files.
Clangd provides a configuration mechanism starting from version 11, which allows among other things loading multiple compile_commands.json
files and customizing compilation flags for the code model. A complete documentation for this mechanism can be found here
To set up a clangd configuration file, you just have to create a .clangd
file in your project folder or one of its ancestors (e.g. if you're working on two sibling folders, you should have the .clangd
file on their parent folder).
Then, to have VSCode's clangd extension to pick up you configuration file, you should add --enable-config
among your clangd settings, like this:
"clangd.arguments": [
... //
"--enable-config"
]
To customize compilation flags we need to use a CompileFlags
section in our configuration files. The section supports the following options:
Add
: to append compilation flags to the compile commandsRemove
: to remove compilation flagsCompilationDatabase
: to specify a custom folder where to fetch the compilation database (compile_commands.json
)Compiler
: allows to specify the compiler executable who needs to run (clang
, clang++
, clang-cl
, ...).Here's an example to add custom flags:
CompileFlags:
Add: [-Wall, -Wextras, -Wno-c++98-compat, -Wno-pre-c++17-compat]
And if you want to add specific flags for a given file or a given folder you can wrap the CompileFlags
section into an If
section:
If:
PathMatch: .*/sourcesubdir/.*\.cpp
CompilerFlags:
Add: [...]
Using the conditional If
section you can get one step ahead and configure clangd to correctly process all our source files in a multi-folder workspace.
First, you just set up the cmake configuration to generate a compiler database using the guide shown in the previous sections.
Then you set up the clangd configuration file to correctly map different compilation database with the correct subfolder in you workspace, as shown in the following example:
If:
PathMatch: Project1/.*
CompileFlags:
CompilationDatabase: Project1 // the parent folder for your compile_commands.json
// You can optionally customize specific compiler flags for Project1 here
---
If:
PathMatch: Project2/.*
CompileFlags:
CompilationDatabase: Project2
// You can optionally customize specific compiler flags for Project2 here
If your clangd extension doesn’t seem to be working properly, here’s a few troubleshooting tips:
That’s it!
VS Code is an amazing tool and we’re always learning new ways to optimize our development environment. If you’ve got any related tips to share, leave a comment!
About KDAB
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Upgrade your applications from Qt 5 to Qt 6 with KDAB’s migration services. Get a free migration assessment and join a hands-on workshop to prepare your team for a successful transition!
Learn more
Learn Modern C++
Our hands-on Modern C++ training courses are designed to quickly familiarize newcomers with the language. They also update professional C++ developers on the latest changes in the language and standard library introduced in recent C++ editions.
Learn more