I am new to programming in general so I decided that I would start by making a simple vector class in C++. However I would like to get in to good habits from the start rather than trying to modify my workflow later on.
I currently have only two files vector3.hpp
and vector3.cpp
. This project will slowly start to grow (making it much more of a general linear algebra library) as I become more familiar with everything, so I would like to adopt a "standard" project layout to make life easier later on. So after looking around I have found two ways to go about organizing hpp and cpp files, the first being:
project
└── src
├── vector3.hpp
└── vector3.cpp
and the second being:
project
├── inc
│ └── project
│ └── vector3.hpp
└── src
└── vector3.cpp
Which would you recommend and why?
Secondly I would like to use the Google C++ Testing Framework for unit testing my code as it seems fairly easy to use. Do you suggest bundling this with my code, for example in a inc/gtest
or contrib/gtest
folder? If bundled, do you suggest using the fuse_gtest_files.py
script to reduce the number or files, or leaving it as is? If not bundled how is this dependency handled?
When it comes to writing tests, how are these generally organized? I was thinking to have one cpp file for each class (test_vector3.cpp
for example) but all compiled in to one binary so that they can all be run together easily?
Since the gtest library is generally build using cmake and make, I was thinking that it would make sense for my project to also be built like this? If I decided to use the following project layout:
├── CMakeLists.txt
├── contrib
│ └── gtest
│ ├── gtest-all.cc
│ └── gtest.h
├── docs
│ └── Doxyfile
├── inc
│ └── project
│ └── vector3.cpp
├── src
│ └── vector3.cpp
└── test
└── test_vector3.cpp
How would the CMakeLists.txt
have to look so that it can either build just the library or the library and the tests? Also I have seen quite a few projects that have a build
and a bin
directory. Does the build happen in the build directory and then the binaries moved out in to the bin directory? Would the binaries for the tests and the library live in the same place? Or would it make more sense to structure it as follows:
test
├── bin
├── build
└── src
└── test_vector3.cpp
I would also like to use doxygen to document my code. Is it possible to get this to automatically run with cmake and make?
Sorry for so many questions, but I have not found a book on C++ that satisfactorily answers these type of questions.
Best Answer
C++ build systems are a bit of a black art and the older the project the more weird stuff you can find so it is not surprising that a lot of questions come up. I'll try to walk through the questions one by one and mention some general things regarding building C++ libraries.
Separating headers and cpp files in directories. This is only essential if you are building a component that is supposed to be used as a library as opposed to an actual application. Your headers are the basis for users to interact with what you offer and must be installed. This means they have to be in a subdirectory (no-one wants lots of headers ending up in top-level
/usr/include/
) and your headers must be able to include themselves with such a setup.works well, because include paths work out and you can use easy globbing for install targets.
Bundling dependencies: I think this largely depends on the ability of the build system to locate and configure dependencies and how dependent your code on a single version is. It also depends on how able your users are and how easy is the dependency to install on their platform. CMake comes with a
find_package
script for Google Test. This makes things a lot easier. I would go with bundling only when necessary and avoid it otherwise.How to build: Avoid in-source builds. CMake makes out of source-builds easy and it makes life a lot easier.
I suppose you also want to use CTest to run tests for your system (it also comes with build-in support for GTest). An important decision for directory layout and test organization will be: Do you end up with subprojects? If so, you need some more work when setting up CMakeLists and should split your subprojects into subdirectories, each with its own
include
andsrc
files. Maybe even their own doxygen runs and outputs (combining multiple doxygen projects is possible, but not easy or pretty).You will end up with something like this:
where
In case you have sub-components I would suggest adding another hierarchy and use the tree above for each sub-project. Then things get tricky, because you need to decide if sub-components search and configure their dependencies or if you do that in the top-level. This should be decided on a case-by-case basis.
Doxygen: After you managed to go through the configuration dance of doxygen, it is trivial to use CMake
add_custom_command
to add a doc target.This is how my projects end up and I have seen some very similar projects, but of course this is no cure all.
Addendum At some point you will want to generate a
config.hpp
file that contains a version define and maybe a define to some version control identifier (a Git hash or SVN revision number). CMake has modules to automate finding that information and to generate files. You can use CMake'sconfigure_file
to replace variables in a template file with variables defined inside theCMakeLists.txt
.If you are building libraries you will also need an export define to get the difference between compilers right, e.g.
__declspec
on MSVC andvisibility
attributes on GCC/clang.