Monorepo with C++: Code Organization for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What is a monorepo and why is it needed in C++?



A monorepo is an approach to code management where all project components, libraries, utilities, and even documentation are stored in a single repository. For C++ projects, this is especially relevant, as the language traditionally requires a clear file and dependency structure.



Advantages of a monorepo for C++:

  • Single version of dependencies — all modules use the same version of libraries, eliminating ABI conflicts.
  • Simplified build — one CMakeLists.txt or build system manages the entire project.
  • Atomic commits — changes in multiple modules can be combined into a single commit, simplifying code review.
  • Easy refactoring — changing an API in one place is immediately visible in all dependent modules.


However, a monorepo is not suitable for everyone: if teams are completely independent and use different compiler versions, a multirepo is better to consider. For beginners, though, a monorepo is an excellent way to avoid the headache of configuring paths and dependencies.



Main part: creating a monorepo for a C++ project



1. Directory structure



A proper structure is the foundation of any monorepo. The recommended template for C++:



my_monorepo/├── CMakeLists.txt          # Root CMake file├── cmake/                  # Auxiliary CMake modules│   └── helpers.cmake├── libs/                   # Libraries│   ├── core/               # Project core│   │   ├── CMakeLists.txt│   │   ├── include/core/│   │   └── src/│   └── utils/              # Utilities│       ├── CMakeLists.txt│       ├── include/utils/│       └── src/├── apps/                   # Executable applications│   ├── main_app/│   │   ├── CMakeLists.txt│   │   └── main.cpp│   └── test_app/│       ├── CMakeLists.txt│       └── main.cpp├── tests/                  # Tests (if not built into modules)│   └── test_core.cpp├── scripts/                # Build and CI scripts│   └── build.sh├── .gitignore└── README.md


Key rule: each module (library or application) must be self-contained. It contains its own CMakeLists.txt, header files in include/, and source files in src/.



2. Configuring the root CMakeLists.txt



The root CMakeLists.txt manages the entire build. It should define common compiler settings, the C++ standard, and include child modules:



cmake_minimum_required(VERSION 3.20)project(MyMonorepo VERSION 1.0.0 LANGUAGES CXX)

# Set a single C++17 standard for the entire projectset(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)set(CMAKE_CXX_EXTENSIONS OFF)

# Option for building testsoption(BUILD_TESTS "Build tests" OFF)

# Include auxiliary modulesinclude(cmake/helpers.cmake)

# Add subdirectories with librariesadd_subdirectory(libs/core)add_subdirectory(libs/utils)

# Add applicationsadd_subdirectory(apps/main_app)add_subdirectory(apps/test_app)

# If tests are enabled, include themif(BUILD_TESTS) enable_testing() add_subdirectory(tests)endif()


3. Example library: libs/core



Consider the core library, which contains a class for working with numbers. File structure:



libs/core/├── CMakeLists.txt├── include/core/│   └── calculator.h└── src/    └── calculator.cpp


Contents of include/core/calculator.h:



#ifndef CORE_CALCULATOR_H#define CORE_CALCULATOR_H

namespace core {

class Calculator {public: Calculator() = default; int add(int a, int b); int multiply(int a, int b);};

} // namespace core

#endif // CORE_CALCULATOR_H


Blogs

Book Recommendations