I am looking for a way to reduce compile-time for header-only libraries. If there are only headers, each translation unit must compile all functions it needs, hence there is a lot of duplicate work done in the compilation step. My idea is to move the compilation to a single translation unit, which creates a single object file the other translation units can link to. This allows the header-only library to effectively behave like a static library which is compiled along with the actual productive code.
A typical header of the library looks like this. It includes all the declarations (all inline
of course), while the definitions are hidden behind the macro MYUTILS_INLINE
.
// MyUtils1.hpp, example utility header
#ifndef MyUtils1_h__
#define MyUtils1_h__
class MyClass1{...};
inline int myfunction1(void);
#ifdef MYUTILS_INLINE
#include MyUtils1.cpp
#endif // MYUTILS_INLINE
#endif // MyUtils1_h__
MyUtils1.cpp
can include additional headers required for the implementation and contains the definitions.
// MyUtils1.cpp, example implementation file
#include MyUtils1.hpp
#include "additionalheader.hpp"
MyClass1::MyClass1(void) {...}
inline int myfunction1(void){...}
In my code, the headers of the library can be included normally. There is one additional file MyUtils.cpp
or MyUtils.inl
, which is the only file which sets MYUTILS_INLINE
and hence sees the definitions and compiles them:
// MyUtils.cpp, separate translation unit in my project
// set macro to get the definitions and compile them.
#define MYUTILS_INLINE
// include all headers which are used throughout the project
#include "MyUtils1.hpp"
#include "MyUtils2.hpp"
#include "MyUtils3.hpp"
Advantages:
- reduced compile-time due to no duplicate work
- fewer header dependencies, headers needed for definitions can be hidden in
MyUtils1.cpp
- opt-in behavior: If user defines
MYUTILS_INLINE
globally, he does not needMyUtils.cpp
and all works as before
Disadvantages:
- less optimization potential because functions are no longer inline
- ship twice as many files (one implementation file for each header)
Now before I restructure my entire library, I want to ask for thoughts about this:
- Did I just reinvent something?
- Do I miss some important points? Advantages or disadvantages?
- Is there a similar or better way?
I noticed that Catch actually does something similar with CATCH_CONFIG_MAIN
.
EDIT
I have read some articles about precompiled headers and tried them in one of my projects. While they try to solve the same problem, they have other issues, especially regarding clean dependencies between header files. My suggested approach is considerably different, as each file still manages its own dependencies but has the option to pass work to the translation unit MyUtils.cpp
.