cpp
exercises
exercises.cpp⚙️cpp
/**
* CMake Exercises
*
* These exercises guide you through creating CMake projects.
* Each exercise builds on the previous one.
*/
#include <iostream>
#include <string>
// ============================================================================
// EXERCISE 1: Minimal CMake Project
// ============================================================================
/*
* Create a minimal CMake project that compiles a "Hello, World!" program.
*
* Requirements:
* 1. Create a directory structure:
* exercise1/
* ├── CMakeLists.txt
* └── main.cpp
*
* 2. CMakeLists.txt should:
* - Set minimum CMake version to 3.16
* - Define project named "HelloWorld"
* - Create executable "hello" from main.cpp
* - Set C++17 standard
*
* 3. Build and run:
* mkdir build && cd build
* cmake ..
* cmake --build .
* ./hello
*
* Write your CMakeLists.txt:
*/
// main.cpp for Exercise 1
void exercise1_main() {
std::cout << "Hello, CMake World!" << std::endl;
}
/*
* Expected CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(HelloWorld LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(hello main.cpp)
*/
// ============================================================================
// EXERCISE 2: Multiple Source Files
// ============================================================================
/*
* Create a project with multiple source files.
*
* Directory structure:
* exercise2/
* ├── CMakeLists.txt
* ├── main.cpp
* ├── math_utils.hpp
* └── math_utils.cpp
*
* Requirements:
* 1. Create math_utils with functions: add, subtract, multiply, divide
* 2. main.cpp uses these functions
* 3. CMakeLists.txt includes both source files
*
* Write your CMakeLists.txt to compile both files together.
*/
// math_utils.hpp
namespace math {
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);
}
// math_utils.cpp
/*
#include "math_utils.hpp"
#include <stdexcept>
namespace math {
double add(double a, double b) { return a + b; }
double subtract(double a, double b) { return a - b; }
double multiply(double a, double b) { return a * b; }
double divide(double a, double b) {
if (b == 0) throw std::runtime_error("Division by zero");
return a / b;
}
}
*/
// main.cpp
/*
#include <iostream>
#include "math_utils.hpp"
int main() {
std::cout << "10 + 5 = " << math::add(10, 5) << std::endl;
std::cout << "10 - 5 = " << math::subtract(10, 5) << std::endl;
std::cout << "10 * 5 = " << math::multiply(10, 5) << std::endl;
std::cout << "10 / 5 = " << math::divide(10, 5) << std::endl;
return 0;
}
*/
// ============================================================================
// EXERCISE 3: Create a Library
// ============================================================================
/*
* Create a static library and link it to an executable.
*
* Directory structure:
* exercise3/
* ├── CMakeLists.txt
* ├── include/
* │ └── stringutils.hpp
* ├── src/
* │ └── stringutils.cpp
* └── app/
* └── main.cpp
*
* Requirements:
* 1. Create a library "stringutils" with functions:
* - to_upper(string) -> string
* - to_lower(string) -> string
* - trim(string) -> string
* 2. Set include directories properly
* 3. Link library to executable
*
* Write your CMakeLists.txt:
*/
/*
* Expected CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(StringUtils LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Create library
add_library(stringutils src/stringutils.cpp)
target_include_directories(stringutils
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Create executable
add_executable(string_app app/main.cpp)
# Link library to executable
target_link_libraries(string_app PRIVATE stringutils)
*/
// ============================================================================
// EXERCISE 4: Compiler Warnings and Options
// ============================================================================
/*
* Add comprehensive compiler warnings to your project.
*
* Requirements:
* 1. Add warnings for GCC/Clang: -Wall -Wextra -Wpedantic -Werror
* 2. Add warnings for MSVC: /W4 /WX
* 3. Use generator expressions for cross-platform support
* 4. Add DEBUG_MODE definition in Debug builds
*
* Add these lines to your CMakeLists.txt:
*/
/*
* Expected additions:
# Compiler warnings (add to any target)
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic -Werror>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wpedantic -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)
# Debug definitions
target_compile_definitions(myapp PRIVATE
$<$<CONFIG:Debug>:DEBUG_MODE>
$<$<CONFIG:Release>:NDEBUG>
)
*/
// ============================================================================
// EXERCISE 5: Subdirectories
// ============================================================================
/*
* Create a project with multiple subdirectories.
*
* Directory structure:
* exercise5/
* ├── CMakeLists.txt (root)
* ├── libs/
* │ ├── CMakeLists.txt
* │ ├── math/
* │ │ ├── CMakeLists.txt
* │ │ ├── math.hpp
* │ │ └── math.cpp
* │ └── io/
* │ ├── CMakeLists.txt
* │ ├── io.hpp
* │ └── io.cpp
* └── app/
* ├── CMakeLists.txt
* └── main.cpp
*
* Requirements:
* 1. Root CMakeLists.txt adds subdirectories
* 2. Each library has its own CMakeLists.txt
* 3. App links to both libraries
*
* Write CMakeLists.txt for each directory:
*/
/*
* Root CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(MultiLib LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(libs)
add_subdirectory(app)
*/
/*
* libs/CMakeLists.txt:
add_subdirectory(math)
add_subdirectory(io)
*/
/*
* libs/math/CMakeLists.txt:
add_library(mathlib math.cpp)
target_include_directories(mathlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
*/
/*
* libs/io/CMakeLists.txt:
add_library(iolib io.cpp)
target_include_directories(iolib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
*/
/*
* app/CMakeLists.txt:
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mathlib iolib)
*/
// ============================================================================
// EXERCISE 6: Find Package
// ============================================================================
/*
* Create a project that uses an external package (Threads).
*
* Requirements:
* 1. Find the Threads package
* 2. Create a multi-threaded application
* 3. Link against Threads::Threads
*
* Write your CMakeLists.txt:
*/
// main.cpp for threading example
#include <thread>
#include <vector>
#include <mutex>
void exercise6_demo() {
std::mutex mtx;
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back([&mtx, i]() {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Thread " << i << " running" << std::endl;
});
}
for (auto& t : threads) {
t.join();
}
}
/*
* Expected CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(ThreadedApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Threads REQUIRED)
add_executable(threaded_app main.cpp)
target_link_libraries(threaded_app PRIVATE Threads::Threads)
*/
// ============================================================================
// EXERCISE 7: FetchContent
// ============================================================================
/*
* Use FetchContent to download and use nlohmann/json library.
*
* Requirements:
* 1. Fetch nlohmann/json from GitHub
* 2. Create an app that uses the library
* 3. Parse and create JSON
*
* Write your CMakeLists.txt:
*/
/*
* Expected CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(JsonApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.2
)
FetchContent_MakeAvailable(json)
add_executable(json_app main.cpp)
target_link_libraries(json_app PRIVATE nlohmann_json::nlohmann_json)
*/
// Example usage:
/*
#include <nlohmann/json.hpp>
#include <iostream>
int main() {
nlohmann::json j;
j["name"] = "John";
j["age"] = 30;
j["scores"] = {95, 87, 92};
std::cout << j.dump(2) << std::endl;
return 0;
}
*/
// ============================================================================
// EXERCISE 8: Options and Conditionals
// ============================================================================
/*
* Create a project with configurable options.
*
* Requirements:
* 1. Option to enable/disable tests: BUILD_TESTS
* 2. Option to enable/disable examples: BUILD_EXAMPLES
* 3. Cache variable for log level: LOG_LEVEL (0-3)
* 4. Conditionally add subdirectories based on options
*
* Write your CMakeLists.txt:
*/
/*
* Expected CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(ConfigurableProject LANGUAGES CXX)
# Options
option(BUILD_TESTS "Build unit tests" ON)
option(BUILD_EXAMPLES "Build example programs" ON)
set(LOG_LEVEL "1" CACHE STRING "Logging level (0=none, 3=verbose)")
# Main library
add_library(mylib src/lib.cpp)
# Conditionally add tests
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# Conditionally add examples
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
# Use the log level
target_compile_definitions(mylib PRIVATE LOG_LEVEL=${LOG_LEVEL})
*/
// ============================================================================
// EXERCISE 9: Installation
// ============================================================================
/*
* Add installation rules to your project.
*
* Requirements:
* 1. Install the library to lib/
* 2. Install headers to include/
* 3. Install executable to bin/
* 4. Generate and install CMake config files
*
* Add installation rules to your CMakeLists.txt:
*/
/*
* Expected additions:
include(GNUInstallDirs)
# Install targets
install(TARGETS mylib myapp
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# Install headers
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# Install to custom prefix:
# cmake --install build --prefix /my/install/path
*/
// ============================================================================
// EXERCISE 10: Complete Project
// ============================================================================
/*
* Create a complete, well-structured CMake project.
*
* Requirements:
* 1. A library with public headers
* 2. An executable using the library
* 3. Unit tests (optional, with option)
* 4. Compiler warnings enabled
* 5. C++17 standard
* 6. Installation rules
* 7. Version information
*
* Directory structure:
* myproject/
* ├── CMakeLists.txt
* ├── cmake/
* │ └── myprojectConfig.cmake.in
* ├── include/
* │ └── myproject/
* │ └── api.hpp
* ├── src/
* │ ├── api.cpp
* │ └── main.cpp
* └── tests/
* ├── CMakeLists.txt
* └── test_api.cpp
*
* Write a complete CMakeLists.txt:
*/
/*
* Full CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(MyProject
VERSION 1.0.0
DESCRIPTION "A complete CMake project example"
LANGUAGES CXX
)
# Global settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Options
option(BUILD_TESTS "Build unit tests" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
# Create library
add_library(myproject_lib src/api.cpp)
add_library(MyProject::lib ALIAS myproject_lib)
target_include_directories(myproject_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_options(myproject_lib PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# Create executable
add_executable(myproject src/main.cpp)
target_link_libraries(myproject PRIVATE myproject_lib)
# Tests
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# Installation
include(GNUInstallDirs)
install(TARGETS myproject_lib myproject
EXPORT MyProjectTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT MyProjectTargets
FILE MyProjectTargets.cmake
NAMESPACE MyProject::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
)
# Package config
include(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/MyProjectConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
)
*/
// ============================================================================
// MAIN
// ============================================================================
int main() {
std::cout << "╔══════════════════════════════════════════════════════════════╗" << std::endl;
std::cout << "║ CMAKE EXERCISES ║" << std::endl;
std::cout << "╚══════════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "\nThese exercises guide you through creating CMake projects." << std::endl;
std::cout << "Each exercise builds on the previous one." << std::endl;
std::cout << "\nExercises:" << std::endl;
std::cout << " 1. Minimal CMake Project" << std::endl;
std::cout << " 2. Multiple Source Files" << std::endl;
std::cout << " 3. Create a Library" << std::endl;
std::cout << " 4. Compiler Warnings" << std::endl;
std::cout << " 5. Subdirectories" << std::endl;
std::cout << " 6. Find Package (Threads)" << std::endl;
std::cout << " 7. FetchContent" << std::endl;
std::cout << " 8. Options and Conditionals" << std::endl;
std::cout << " 9. Installation" << std::endl;
std::cout << " 10. Complete Project" << std::endl;
std::cout << "\n═══════════════════════════════════════════════════════════════" << std::endl;
std::cout << "Create each exercise in its own directory and test!" << std::endl;
std::cout << "Build commands:" << std::endl;
std::cout << " mkdir build && cd build" << std::endl;
std::cout << " cmake .." << std::endl;
std::cout << " cmake --build ." << std::endl;
return 0;
}