Initial chalenges
I decided to create speech recognition library in C++ and I faced some challenges:
I'm new to C++ so this simple task how to structure my project code wasn't straight forward. An idea how C++ project structure should look like I got looking at open source C++ libraries. Details what the idea is will be discussed in other sections.
- how C++ project structure should look
- how to make cross-platform builds
I'm new to C++ so this simple task how to structure my project code wasn't straight forward. An idea how C++ project structure should look like I got looking at open source C++ libraries. Details what the idea is will be discussed in other sections.
For cross-platform builds I decided to use CMake which after some investigation looked to me quite widely used and well suited for my project. I basically will be using XCode as IDE and CMake can make XCode project from source code as well as Visual Studio project and so on.
Simple project structure
The main idea about project structure is that you have at least 2 folders
Idea example:
include
and src
. Folders purpose is:
include
- PUBLIC header files (.h files).src
- PRIVATE source files (.h and .m files).test
- tests files if you write tests (indefinitely you should).libs
- third party or your own libraries you depend on.
include
should be under folder named after your library domain. Reason behind this is that when you expose public header files you expose only include
directory and when you #include
files from library you do this #inlcude <HMM/Algorithm.h>
instead of #include "Algorithm.h"
if it was in root of include
.Idea example:
{library_name}
├── include
│ └── {library_domain}
├── src
├── test
└── libs
Concrete example:
HMM
├── include
│ └── HMM
│ └── Algorithm.h
├── src
│ ├── Algorithm.cpp
│ ├── FancyMath.h
│ └── FancyMath.cpp
└── test
├── AlgorithmTests.cpp
└── FancyMathTests.cpp
In concrete example we have HMM library which exposed only
Algorithm.h
header file. FancyMath
files are private and not exposed. Library domain is HMM, so when you want to include public header for example Algorithm.h
you do it this way: #inlcude <HMM/Algorithm.h>
instead of #include "Algorithm.h"
.Advanced project structure
When you have a very complex project with third party libraries dependencies you need a way to link it all together. For this purpose there is
Concrete example:
libs
folder. You basically put there third party libraries which have project structure like in simple example above.Concrete example:
Foo
├── include
│ └── Foo
│ ├── Foo.h
│ └── ...
├── src
│ ├── Foo.cpp
│ └── ...
├── test
│ └── ...
└── libs
├── A
│ ├── include
│ │ └── A
│ │ ├── A.h
│ │ └── ...
│ ├── src
│ │ ├── A.cpp
│ │ └── ...
│ └── test
│ └── ...
└── B
├── include
│ └── B
│ ├── B.h
│ └── ...
├── src
│ ├── B.cpp
│ └── ...
└── test
└── ...
CMake
At this point you should have good understanding how project structure should look like and so CMake comes to game. The building blocks of CMake are
This basically let me import
GTest target check example
CMakeLists.txt
files. You basically write those CMakeLists files and they define what should be included into generated build system. I won't go into details how CMake work, but instead I will note some thins that I came across. Link to Github repo with example is in Reference section, check it out!PRIVATE/PUBLIC headers
As I mentioned in previous sections I wanted to import library with
#include <HMM/Algorithm.h>
instead of #include "Algorithm.h"
when library is exposed to world. But when Algorithm.h
is used internally I want to import it as #include "Algorithm.h"
, because it is my library scope. Thats defined in CMake as follows:set(LIBRARY_NAME
HMM
)
target_include_directories(${LIBRARY_NAME} PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/${LIBRARY_NAME}>
$<INSTALL_INTERFACE:include/${LIBRARY_NAME}>
)
target_include_directories(${LIBRARY_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
This basically let me import
Algorithm.h
as #include "Algorithm.h"
when I used it internally in my library and as #include <HMM/Algorithm.h>
when library is exposed to someone else.Google Test - GTest
I believe the biggest headache was with GTest, because I wanted that both static libraries A and B to have tests and when I added GTest library two times in A and B libraries I got error, because two identical GTest targets can't exist so I needed workaround. The solution was only add GTest once in Foo and check for existing GTest target in A and B libraries. This worked like a charm!
Project structure example
Project structure example
Foo
├── include
├── src
├── test
└── libs
├── A
├── B
└── gtest-1.7.0
GTest target check example
if (NOT (TARGET gtest AND TARGET gtest_main))
message(FATAL_ERROR "gtest and gtest_main targets NOT found")
endif()
I wont go to details how to use GTest with CMake, you can check it in example project.
Reference
- CMake example. Github.
No comments:
Post a Comment