Explore Developer Center's New Chatbot! MongoDB AI Chatbot can be accessed at the top of your navigation to answer all your MongoDB questions.

Join us at AWS re:Invent 2024! Learn how to use MongoDB for AI use cases.
MongoDB Developer
C++
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Languageschevron-right
C++chevron-right

CMake + Conan + VS Code

Jorge D. Ortiz-Fuentes6 min read • Published Sep 04, 2024 • Updated Sep 04, 2024
CC++
Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Hey there, C and C++ developers! Today, I want to talk about a very sensitive topic for all of us. A thing that has caused pain and shame in the community. A thing that has affected our productivity and sometimes even our mental health. I am not referring to compiler errors, but to building complex projects and using third-party dependencies in them.
Traditionally, we used Makefiles to build our C and C++ projects and depended on having our third-party dependencies installed in our development systems or their sources included in our project in one way or another. The tools have evolved a lot through the last couple of decades, but not everybody is using them completely or even at all to build their projects.
Unless you live under a rock, you have probably seen what your fellow developers do when they use other powerful backend programming languages. Golang has support for third-party modules built-in. You just mention the URL of the third-party repository and it takes care of the rest. Jealous? Rust's cargo tool takes care of building your project and managing all its dependencies even when the project is distributed into several workspaces. Envious? Ashamed no more! Let me show you how to use CMake and Conan within VS Code.
This article is aimed at C or C++ developers who work on projects that produce binaries or libraries and want to be able to use third-party dependencies. In our case, we will be using the MongoDB driver for C++.
In this article, I will:
  • Create an example project from scratch with a directory structure that can be used to work with one or more libraries and a binary.
  • Write the instructions to build the different pieces of the project and link them together.
  • Create the configuration to use a third-party dependency using a package manager.
  • Put all these pieces together and make them work with VS Code.

Your project

We are going to focus on a project that produces a library, which uses a third-party package. The library is then used together with some other source code to produce a binary. The same structure can be extended to have more libraries or even more binaries. In this case, the third-party package is the MongoDB driver, which allows you to connect to a MongoDB Atlas cluster or a local MongoDB database.
  1. This is the basic directory structure:
    1├── LibraryN
    2│ ├── include
    3│ │ └── LibraryN.h
    4│ └── src
    5│ └── LibraryN.cpp
    6└── src
    7 └── main.cpp
  2. In the header libraryN.h, we add the traditional protection to avoid multiple inclusion:
    1#ifndef _LIBRARY_N_H_
    2#define _LIBRARY_N_H_
    3
    4#endif
  3. Inside of this preprocessor directives, we are going to define a class:
    1class MDBClient {
    2};
  4. And its constructor:
    1public:
    2MDBClient();
  5. Now, we are going to write the implementation code for this library in libraryN.cpp. We start by including the header that we have just declared:
    1#include "libraryN.h"
  6. Then, we define the implementation of the constructor that we declared in the header:
    1MDBClient::MDBClient()
    2{
    3}
  7. To be able to use the MongoDB driver here, we need to include the header for the desired part of the driver. In this case, we are going to create an instance:
    1#include <mongocxx/instance.hpp>
  8. We can now use the instance in the constructor:
    1mongocxx::instance inst{};
  9. And finally, we have to write the code that uses the library from the executable. We include the header of the library in main.cpp:
    1#include "libraryN.h"
  10. We declare the main function here, which returns 0:
    1int main(int argc, char const *argv[])
    2{
    3 return 0;
    4}
  11. And we finish by creating an instance of the class that we defined in the library:
    1auto mc = MDBClient{};

Create the instructions to build

We are going to use CMake as the build system for our project. The instructions to CMake are provided in the CMakeLists.txt.
  1. We start by defining the minimum version of CMake that we allow to be used with our project.
    1cmake_minimum_required(VERSION 3.15)
  2. Then, we define the project using a name, a version and the language(s). This would also work for a C project, but in this case, we are going to use C++:
    1project(CoolProject VERSION 0.1 LANGUAGES CXX)
  3. The first actual instructions to build the project will be used to tell CMake to include our library. First, we include the sources –a single file, in this case– and then we provide the corresponding header files.
    1add_library(libraryN STATIC LibraryN/src/LibraryN.cpp)
    2target_include_directories(libraryN PUBLIC LibraryN/include)
  4. Finally, we add the code for the executable and link the library that we defined in the previous step.
    1add_executable(${PROJECT_NAME} src/main.cpp)
    2target_link_libraries(${PROJECT_NAME}
    3 PRIVATE
    4 libraryN)

Configure the dependencies

The build instructions that we have just written would be suitable for projects with one library and an executable. You could add more libraries and other executables using the same CMake commands with the corresponding data. But if you want to bring in a third-party dependency, your options with CMake are more painful. This is where Conan comes to our rescue.
Conan is an open-source package manager for C and C++ that simplifies the process of integrating third-party dependencies in our project. It is an alternative to Microsoft's vcpkg, but they have slightly different approaches. They both have repositories of third-party dependencies and can be configured to include the ones that you need in your project. However, while vcpkg uses a series of tags that define the versions of the dependencies in the repository that are tested to work together, Conan allows you to choose independent versions for each dependency. You can choose individual versions with vcpkg, but it requires some extra effort.
  1. We must have Conan installed. In my case, I went with Homebrew.
  2. The first thing we need to do is to check if the package we want to use –MongoDB C++ driver– is available in Conan Center. So we visit that page and type "mongo" in the search box. Conan Center offers both the mongo-c-driver and the mongo-cxx-driver. We will use the second, so click on it. Conan center recipes
  3. On the mongo-cxx-driver page, it suggests using a simple conanfile.txt or a conanfile.py. We will go with the file with "txt" extension, because although it is less versatile than the Python configuration, it is enough for our needs in this example. So, let's create a conanfile.txt with this content:
    1[requires]
    2mongo-cxx-driver/3.8.1
    3[generators]
    4CMakeDeps
    5CMakeToolchain
    6[layout]
    7cmake_layout

Project setup

Also, the page for the dependency suggests adding a couple of lines to our CMake configuration.
  1. We add a line to the CMakeLists.txt, right after the project declaration line, to find the package that Conan will install.
    1find_package(mongocxx REQUIRED)
  2. Add a line to the bottom of the block for the library that will instruct CMake to link the dependency to it. We link the dependency to the library and not the executable because this is where we use the dependency.
    1target_link_libraries(libraryN
    2 PRIVATE
    3 mongo::mongocxx_static)
  3. We use the command line to finish setting up the project. First, we create the profile that contains information about our toolchain and the type of build, which is going to be "Release." You can create a debug profile by copying the file that has been created and changing the build_type to "Debug" in the new file.
    1conan profile detect # If you don't have a conan profile yet
  4. Then, we can install the packages and tell Conan to produce the configuration. You can use the next command again with --profile=debug (or the filename of your debug profile), if you also want to also install the debug version of the packages.
    1conan install . --build=missing
  5. At this point, we should be able to build the project from the command line using the preset created by Conan. We can list CMake presets using the following command:
    1cmake --list-presets
  6. There should be only one configuration preset available –or two if you also installed the debug one–, so let's use it to configure the project:
    1cmake --preset conan-release
  7. And we can build the project using the corresponding build preset:
    1cmake --build --preset conan-release
  8. Hug your partner, your friend, or your pet, because you have successfully built the project. Yippee ki-yay, my friend!

Use from VS Code

Let's clean the build to see how this works with VS Code.
  1. We need to specify the directory that Conan has used for the cache.
    1cmake --build build/Release --target clean
  2. And before we try to build from VS Code, we have to be aware that you will need to have two extensions installed: CMake and CMake Tools. VSCode extensions
  3. From the command palette (View -> Command Palette or Cmd+S+P), we invoke "Developer: window reload." VS Code will reload the directory, check the contents of the CMakeLists.txt, and offer us a choice of one of the available profiles. We should choose one of the presets provided by Conan.
  4. To build the project from VS Code, click on the CMake Tools icons on the sidebar. CMake-Tools usage
  5. We should be able to build, run, and even debug without any problems.

Conclusion

In this tutorial, we have explored some options to sharpen our tools. We can now use CMake for building mid-sized projects and Conan to take care of our dependencies. We can use these two to build from the command line, but we have gone a step further and used that configuration from our beloved VS Code.
Stay curious, hack your code, see you next time!
Top Comments in Forums
There are no comments on this article yet.
Start the Conversation

Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

MongoDB Time Series With C++


Sep 17, 2024 | 6 min read
Tutorial

Me and the Devil BlueZ: Reading BLE Sensors From C++


Sep 17, 2024 | 16 min read
Code Example

EnSat


Feb 08, 2023 | 3 min read
Tutorial

Me and the Devil BlueZ: Implementing a BLE Central in Linux - Part 1


Dec 14, 2023 | 10 min read
Table of Contents