commit f35345ab0a95a731a4b4f71219dbae17c0b4f0e7 Author: Petr Hrdina Date: Tue Jul 15 07:41:45 2025 +0200 Add refactored CMake example diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2a10c7f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project("Refactoring") + +set_property(GLOBAL PROPERTY CXX_STANDARD 23) + +# Build modules +add_subdirectory("modules/ModuleA") +add_subdirectory("modules/ModuleB") +add_subdirectory("modules/ModuleC") + +# Build app +add_subdirectory("app") \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..44c6ec4 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Refactoring project + +## How to build + +Build the project by running following commands: +``` +cmake -B build +cmake --build build +``` + +## Static libraries + +The project uses static libraries for the modules. The reassoning behind this is that the main purpose of this refactoring is to modularize the code base. Static linking does not change how the app is compiled or installed. This keeps things simple for now. But for future improvements modules can be linked dynamically to allow of updating individual modules without recompilation if the ABI does not change. + +## Directory structure + +The current directory structure looks like this. + +``` +refactoring +├── app +│   ├── CMakeLists.txt +│   └── src +│   └── main.cpp +├── CMakeLists.txt +├── modules +│   ├── ModuleA +│   │   ├── CMakeLists.txt +│   │   ├── include +│   │   │   └── moduleA +│   │   │   └── ClassA.h +│   │   └── src +│   │   └── ClassA.cpp +│   ├── ModuleB +│   │   ├── CMakeLists.txt +│   │   ├── include +│   │   │   └── moduleB +│   │   │   └── ClassB.h +│   │   └── src +│   │   └── ClassB.cpp +│   └── ModuleC +│   ├── CMakeLists.txt +│   ├── include +│   │   └── moduleC +│   │   └── ClassC.h +│   └── src +│   └── ClassC.cpp +└── README.md + +16 directories, 13 files +``` + +This is mostly straightforward - app and modules are separated. Each module has its own directory in which CMakeLists.txt specifies how to should be built. + +### Structure of module include directory + +Only maybe unexpected thing is that every module include directory contains another directory e.g. `ModuleA/include/moduleA/ClassA.h`. This is to prevent conflicts when including files with same name from different modules. It also allows be visible from which module the included file in the app is from. + +[app source file](app/src/main.cpp): +``` +// app/main.cpp +`#include "moduleA/ClassA.h"` +``` + +If there are many modules a programmer of the app does not get lost where did the class come from. This makes it easy to locate the file without the need for searching. +In the module itself it most inner directory is included so that we don't need to specify the module. + +[module source file](modules/ModuleA/src/ClassA.cpp): +``` +// ModuleA/src/ClassA.cpp +#include "ClassA.h" +``` \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..6f8fd3e --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,8 @@ +set(MY_EXE "app") + +add_executable(${MY_EXE} "src/main.cpp") + +target_link_libraries(${MY_EXE} "moduleA" "moduleB" "moduleC") + +# no need to include directories, because they are included automatically +# thanks to exposing the directories using PUBLIC \ No newline at end of file diff --git a/app/src/main.cpp b/app/src/main.cpp new file mode 100644 index 0000000..f5cad40 --- /dev/null +++ b/app/src/main.cpp @@ -0,0 +1,10 @@ +#include "moduleA/ClassA.h" +#include "moduleB/ClassB.h" +#include "moduleC/ClassC.h" + +int main(int argc, char *argv[]) +{ + ClassA::hello(); + ClassB::hello(); + ClassC::hello(); +} diff --git a/modules/ModuleA/CMakeLists.txt b/modules/ModuleA/CMakeLists.txt new file mode 100644 index 0000000..a7d8aec --- /dev/null +++ b/modules/ModuleA/CMakeLists.txt @@ -0,0 +1,7 @@ +set(MY_LIB "moduleA") + +add_library(${MY_LIB} STATIC "src/ClassA.cpp") + +target_include_directories(${MY_LIB} PRIVATE "include/moduleA") + +target_include_directories(${MY_LIB} PUBLIC "include") \ No newline at end of file diff --git a/modules/ModuleA/include/moduleA/ClassA.h b/modules/ModuleA/include/moduleA/ClassA.h new file mode 100644 index 0000000..2df77cb --- /dev/null +++ b/modules/ModuleA/include/moduleA/ClassA.h @@ -0,0 +1,9 @@ +#ifndef CLASS_A_ +#define CLASS_A_ + +class ClassA { +public: + static void hello(); +}; + +#endif diff --git a/modules/ModuleA/src/ClassA.cpp b/modules/ModuleA/src/ClassA.cpp new file mode 100644 index 0000000..c2af3e5 --- /dev/null +++ b/modules/ModuleA/src/ClassA.cpp @@ -0,0 +1,4 @@ +#include "ClassA.h" +#include + +void ClassA::hello() { std::cout << "Hello from A" << std::endl; } diff --git a/modules/ModuleB/CMakeLists.txt b/modules/ModuleB/CMakeLists.txt new file mode 100644 index 0000000..928c994 --- /dev/null +++ b/modules/ModuleB/CMakeLists.txt @@ -0,0 +1,7 @@ +set(MY_LIB "moduleB") + +add_library(${MY_LIB} STATIC "src/ClassB.cpp") + +target_include_directories(${MY_LIB} PRIVATE "include/moduleB") + +target_include_directories(${MY_LIB} PUBLIC "include") \ No newline at end of file diff --git a/modules/ModuleB/include/moduleB/ClassB.h b/modules/ModuleB/include/moduleB/ClassB.h new file mode 100644 index 0000000..aecd0f7 --- /dev/null +++ b/modules/ModuleB/include/moduleB/ClassB.h @@ -0,0 +1,9 @@ +#ifndef CLASS_B_ +#define CLASS_B_ + +class ClassB { +public: + static void hello(); +}; + +#endif diff --git a/modules/ModuleB/src/ClassB.cpp b/modules/ModuleB/src/ClassB.cpp new file mode 100644 index 0000000..8d8bc74 --- /dev/null +++ b/modules/ModuleB/src/ClassB.cpp @@ -0,0 +1,4 @@ +#include "ClassB.h" +#include + +void ClassB::hello() { std::cout << "Hello from B" << std::endl; } diff --git a/modules/ModuleC/CMakeLists.txt b/modules/ModuleC/CMakeLists.txt new file mode 100644 index 0000000..ca13536 --- /dev/null +++ b/modules/ModuleC/CMakeLists.txt @@ -0,0 +1,7 @@ +set(MY_LIB "moduleC") + +add_library(${MY_LIB} STATIC "src/ClassC.cpp") + +target_include_directories(${MY_LIB} PRIVATE "include/moduleC") + +target_include_directories(${MY_LIB} PUBLIC "include") \ No newline at end of file diff --git a/modules/ModuleC/include/moduleC/ClassC.h b/modules/ModuleC/include/moduleC/ClassC.h new file mode 100644 index 0000000..caccdbc --- /dev/null +++ b/modules/ModuleC/include/moduleC/ClassC.h @@ -0,0 +1,9 @@ +#ifndef CLASS_C_ +#define CLASS_C_ + +class ClassC { +public: + static void hello(); +}; + +#endif diff --git a/modules/ModuleC/src/ClassC.cpp b/modules/ModuleC/src/ClassC.cpp new file mode 100644 index 0000000..5900306 --- /dev/null +++ b/modules/ModuleC/src/ClassC.cpp @@ -0,0 +1,4 @@ +#include "ClassC.h" +#include + +void ClassC::hello() { std::cout << "Hello from C" << std::endl; }