I'm developing my application using C++ and cmake.
I'd like to check each C++ header file includes required include files correctly.
Here is an example:
a.hpp
inline void func_a() {
}
b.hpp
// #include "a.hpp" is missing
inline void func_b() {
func_a();
}
main.cpp
#include "a.hpp"
#include "b.hpp"
int main() {}
Demo: https://wandbox.org/permlink/kZqoNHMYARIB3bc1
b.hpp should include a.hpp. Let's say b.hpp missing include a.hpp. If main.cpp include a.hpp before b.hpp, no compile error is occurred. If include order is opposite, compile error is occurred.
I'd like to check this kind of problem.
I'm using fly-check on emacs. It checks this problem well. I'd like to some checking mechanism into my cmake build system.
For example, if I execute make depcheck
, then compile error is detected.
I think that if I setup a cmake target that compiles all header files individually but not link, the expected compile error would be reported.
I couldn't find how to setup that, so far.
Is there any way to do that? Or other ways to achieve the goal ?
Each header file should include header files that contain required element. In other words, each header file should compile individually.
I want to know the way to automatically detect b.hpp is missing `#include "a.hpp" by tool assist. The tool means not editor. I guess that cmake could do that. I'm trying to find the way.
To check if an header file has been included or not in a C or C++ code, we need to check if the macro defined in the header file is being defined in the client code. Standard header files like math. h have their own unique macro (like _MATH_H ) which we need to check. Consider this example of checking if math.
Most standard headers are stored in /usr/include .
Generally, you only want to put the minimum necessary includes into a class header file, as anyone else who uses that header will be forced to #include all of them too.
Conventional wisdom is to add source files to every header. Even if b.cpp
includes only this line:
include "b.hpp" // Note, this should be the first include
That way, you can compile every cpp file in isolation, and a successful compilation means the corresponding header is self-contained.
Of course, if you have an implementation file already, then moving the corresponding header to be included first goes towards ensuring that.
@StoryTeller 's answer
Conventional wisdom is to add source files to every header.
is appropriate way to achieve the goal. It requires adding many source files. It is annoying work especially I develop a header only library.
How to automate that process?
I found a way to check missing include file on cmake. The strategy is compile each header files individually and directly.
Here is CMakeLists.txt
cmake_minimum_required(VERSION 3.8.2)
project(test_checker)
add_custom_target(chkdeps)
file(GLOB HDR_ROOT "*.hpp")
FOREACH (HDR ${HDR_ROOT})
message(STATUS "${HDR}")
get_filename_component(HDR_WE ${HDR} NAME_WE)
SET(CHK_TARGET "${HDR_WE}.chk")
add_custom_target(
${CHK_TARGET}
COMMAND ${CMAKE_CXX_COMPILER} -c ${HDR}
VERBATIM
)
add_dependencies(chkdeps ${CHK_TARGET})
ENDFOREACH ()
To check missing include files, execute make chkdeps
.
In order to do only compile, I use add_custom_target. The custom target name is chkdeps
(check dependencies). This is the target for all header files dependency checking.
I get the list of *.hpp using file(GLOB HDR_ROOT "*.hpp")
. For each got files, I add custom target for only compile using add_custom_target.
I add the extension .chk
to avoid conflict. For example if the file name is a.hpp
then the target name is a.chk
.
I execute the COMMAND ${CMAKE_CXX_COMPILER}
with -c
option. The -c
option is for only compile. I only tested the cmake on Linux. I know setting compile option directly is not good for cross platform development. cmake might provides compile only cross platform mechanism. But I couldn't find it, so far.
Then I add dependency to chkdeps
using add_dependencies. Due to this dependency, when I execute make chkdeps
, all custom targets (a.chk
and b.chk
) run.
When I run make chkdeps
, then I got the expected error "'func_a' was not declared in this scope" as follows.
make chkdeps
Built target a.chk
/home/kondo/work/tmp/fly_check/b.hpp: In function 'void func_b()':
/home/kondo/work/tmp/fly_check/b.hpp:3:5: error: 'func_a' was not declared in this scope; did you mean 'func_b'?
3 | func_a();
| ^~~~~~
| func_b
make[3]: *** [CMakeFiles/b.chk.dir/build.make:57: CMakeFiles/b.chk] Error 1
make[2]: *** [CMakeFiles/Makefile2:78: CMakeFiles/b.chk.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:113: CMakeFiles/chkdeps.dir/rule] Error 2
make: *** [Makefile:131: chkdeps] Error 2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With