-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
134: Practice Exercise: Setting Up a CMake Project with Tests
How should I actually organize my folders so CMake doesn't become a mess?
I've seen too many beginners dump everything into one directory. It works for a tiny project, but the moment you add tests, it becomes a nightmare of filenames. The standard professional approach is to separate your "production" code from your "test" code.
For this exercise, imagine we're building a PrimeGenerator utility. I'd set it up like this:
/project_root
├── CMakeLists.txt(The main entry point)
├── src/(Your actual logic)
│ ├── CMakeLists.txt
│ ├── PrimeGenerator.cpp
│ └── PrimeGenerator.hpp
└── tests/(Your test suite)
├── CMakeLists.txt
└── test_main.cpp
By splitting the CMakeLists.txt files, you keep the logic modular. The root file handles the project-wide settings, while the subdirectories handle their own targets.
How do I avoid compiling my source files twice for the app and the tests?
This is where a lot of people trip up. They try to add PrimeGenerator.cpp to both the main executable and the test executable. Not only is that slow, but it can lead to weird linking errors.
The trick is to compile your logic as a library. You create a library target that both your main app and your tests link against. In your src/CMakeLists.txt, it looks like this:
# Create a library from the source files
add_library(PrimeLib PrimeGenerator.cpp)
# Make sure whoever links to this can find the headers in this directory
target_include_directories(PrimeLib PUBLIC .)
Now, in your tests/CMakeLists.txt, you don't list the source files again. You just link the library:
add_executable(unit_tests test_main.cpp)
target_link_libraries(unit_tests PRIVATE PrimeLib)
What's the cleanest way to bring in a testing framework like GoogleTest?
Back in the day, you had to manually install libraries on your system, which made collaborating with other developers a pain. Now, I always use FetchContent. It tells CMake to go grab the framework from GitHub during the configuration step, so the project is "self-contained."
In your root CMakeLists.txt, add this:
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/heads/main.zip
)
# For Windows, this prevents GTest from overriding the runtime library settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
Once that's done, you just link gtest_main to your test executable, and you don't even have to write your own main() function for the tests—GoogleTest provides one for you.
How do I actually run the tests once everything is configured?
You don't just run the test executable manually; you use ctest. It's a command-line tool that comes with CMake designed specifically to run your test suite and report failures in a clean format.
First, you must call enable_testing() in your root CMakeLists.txt. Then, in your tests/CMakeLists.txt, you register the test:
add_test(NAME PrimeTests COMMAND unit_tests)
After you build the project, go to your build directory and simply type:
ctest --output-on-failure
I always add the --output-on-failure flag. Otherwise, CTest just tells you a test failed, but it won't show you the actual error message from the framework, which is incredibly frustrating when you're trying to debug a failing assertion.
📋 Practical Task
Project Setup: Validated Matrix Multiplication Library
Your task is to set up a complete CMake project structure for a Matrix Math library. You need to ensure the project is modular and the tests are decoupled from the main application.
- Step 1: Create the directory structure with
src/andtests/folders. - Step 2: In
src/, implement a simpleMatrixclass that can perform a 2x2 matrix multiplication. - Step 3: Configure the
src/CMakeLists.txtto build this as a library namedMatrixLib. - Step 4: In the root
CMakeLists.txt, useFetchContentto integrate Catch2 or GoogleTest. - Step 5: In
tests/, create a test suite that verifies:- Multiplying a matrix by the Identity matrix returns the original matrix.
- Multiplying two zero matrices returns a zero matrix.
- Step 6: Use
add_testandenable_testing()so that you can execute the entire suite using thectestcommand.
There are no comments for now.