I have a question regarding how to structure a c++ project and build that using CMake (inside of CLion). I am very new to c++ (I started to learn the language only two weeks ago) and to CMake (I haven't ever configured a CMake file). I do however have a bunch of experience with other programming languages and their ecosystems like Java, JavaScript, Php, and .NET—perhaps analogies with those ecosystems could help?
We're building our own (small, 2d) game and game-engine for a school project. The game engine should be shippable on its own, the game should build on our own engine. The engine will have its own dependencies (built using SDL2, thats a requirement).
I imagine the engine will become a (static) library, while the actual game will compile to an executable that depends on the engine library.
For the purposes of simplifying project management we're looking to host both the engine and the game code in the same git repository.
Given that we might want to put a math
module into our engine (probaby not the case, but for the purpose of this question) I imagine a folder structure like the following:
our-project/
├── README.md
├── engine
│ ├── src
│ │ ├── math.cpp
│ │ └── math.h
│ └── test
│ └── ...
└── game
├── src
│ └── main.cpp
└── test
└── ...
The engine would then contain the following code:
// in math.h
namespace engine::math {
int add(int a, int b);
}
// in math.cpp
#include "math.h"
namespace engine::math {
int add(int a, int b) {
return a + b;
}
}
Let's say we wanted to make use of this engine code from our game, I imagne the following code:
// in game/main.cpp
#include <iostream>
#include "engine/math.h"
using namespace engine;
int main() {
std::cout << math::add(10, 1) << std::endl;
}
You declare separate CMake targets, i.e., in engine/src/CMakeLists.txt
:
add_library(engine
math.cpp)
while in game/src/CMakeLists.txt
, you have
add_executable(my-game
main.cpp)
target_link_libraries(my-game
PRIVATE
engine)
When using e.g. make
, you can build the targets individually by
make engine # build only the engine, not the executable
make my-game # build engine if necessary, then build executable
Separate test targets make sense, too.
Include flags in CMake are propagated as follows.
target_include_directories(engine
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR})
The above setup propagates an include directory to all targets that link against engine. This is simple, but has a drawback: you can't distinguish between public headers and implementation details. As an alternative, you could have a directory engine/src/public/engine
with all public headers that shall be used by the game:
target_include_directories(engine
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/public)
target_include_directories(engine
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/public/engine)
This way, client code in game
uses #include "engine/math.h"
, while in engine
, you can just go with #include "math.h"
. I like such a setup, as it's easily visible what is the interface of a library and what is its implementation. But it's also somewhat a matter of taste.
Again opinionated. But I think this is a good directory structure. Stick to it.