Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check each header file includes required include files?

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 ?

my header file inclusion policy

Each header file should include header files that contain required element. In other words, each header file should compile individually.

What I want to achieve

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.

like image 913
Takatoshi Kondo Avatar asked Sep 11 '19 08:09

Takatoshi Kondo


People also ask

How do you check if a header file is included?

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.

Where do I find header files?

Most standard headers are stored in /usr/include .

Do you put includes in header files?

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.


Video Answer


2 Answers

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.

like image 87
StoryTeller - Unslander Monica Avatar answered Oct 17 '22 15:10

StoryTeller - Unslander Monica


@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
like image 42
Takatoshi Kondo Avatar answered Oct 17 '22 16:10

Takatoshi Kondo