DEV Community

Dmitry Lushchan
Dmitry Lushchan

Posted on

Compiling Header Files — or How to Get Doxygen Documentation for Free

Originally written in 2012 and translated in 2025 with minor updates for clarity and relevance.

Who This Article Is For

If you're an experienced C++ developer, you might already know the command below:

gcc -c -x c++ -I ./ */*/*/*/*.h
Enter fullscreen mode Exit fullscreen mode

But if you're just getting started with Doxygen, or you've tried it once and were confused by the output, this article might save you time and frustration.

Background

C++ programming often starts not with writing code, but with reading and understanding someone else's.

I spent several years working on a simulation modeling system. The codebase, while written in C++, had gradually evolved into its own dialect full of macros — a situation many developers will find familiar.

Early in the project, I was advised to generate documentation using Doxygen. For various reasons, that attempt failed — and I didn’t return to it until much later.

Motivation

As my skills improved and the project grew, I wanted to clean up a library I was actively maintaining. My goal was to enforce a consistent structure:

  • One header file per class or closely related group
  • Three file types: headers (*.h), inline implementation files (*.inl), and source files (*.cpp)
  • No function definitions in headers — only in *.cpp or *.inl
  • Unified formatting
  • Doxygen-style comments (\class, \brief, \todo, etc.)

After several weeks of cleaning, refactoring, and commenting, I expected Doxygen to produce beautiful documentation. But instead…

cd Project/doc
doxygen project-doxyfile
cd html/
./index.html
Enter fullscreen mode Exit fullscreen mode

...the result was underwhelming: broken diagrams, empty namespaces, and macros misinterpreted as functions.

Initial Hypothesis: Misconfigured PREDEFINED Option?

I checked all the usual Doxygen settings:

  • ENABLE_PREPROCESSING
  • MACRO_EXPANSION
  • EXPAND_ONLY_PREDEF
  • SEARCH_INCLUDES
  • INCLUDE_PATH

Everything looked reasonable.

The workaround was to manually list the necessary macros using the PREDEFINED option. That worked — temporarily — but it was tedious to keep updating this list.

A Deeper Problem: Header File Assumptions

At one point, I even downloaded the Doxygen source code to investigate. But then I realized a key difference between a compiler and a documentation generator:

The compiler works with translation units — i.e., source files after preprocessing. It only "sees" header files through the #include directives present in those units. Doxygen, on the other hand, treats each header file as a standalone unit.

This leads to a critical insight:

A header file that compiles fine in context may still be invalid in isolation — and that's what breaks Doxygen.

What was wrong with our headers? They were missing explicit #include directives for the macros and types they used. These dependencies were pulled in indirectly during normal compilation, so the compiler never complained — but Doxygen did.

The Fix: Make Header Files Compile

Our solution was simple and effective:

Treat every header file as a standalone translation unit and try compiling it.

That way, any missing includes or broken declarations are caught — and once fixed, Doxygen has no excuse to fail.

Another helpful practice is to make sure each header file is included as the very first #include in its corresponding .cpp file. This enforces that the header is self-contained and reveals missing dependencies immediately during compilation.

The Implementation

At the time, our team had just migrated from Visual Studio 2008 to GCC 4.6 on Linux. I discovered that using -I to set the include path — and carefully applying file globbing — allowed us to compile all header files directly:

g++ -I ./ */*/*/*/*.h */*/*/*.h */*/*.h
Enter fullscreen mode Exit fullscreen mode

However, to avoid linking errors and specify the language, we added two more flags:

gcc -c -x c++ -I ./ */*/*/*/*.h */*/*/*.h */*/*.h
Enter fullscreen mode Exit fullscreen mode

This command performs a syntax check (without linking) on all header files.

We skipped *.inl files in this step, as they’re not meant to be compiled on their own.

Once compilation errors were resolved, Doxygen started producing accurate diagrams and documentation.

Optional Improvements

We could automate this further using CMake, or add a script to validate headers during CI. But even this basic technique drastically improved our documentation quality.

Conclusion

Why is this article called "...or How to Get Documentation for Free"?

Because even without a single Doxygen-style comment, you can still generate navigable, visual documentation from your headers — if they’re self-contained and compile cleanly.

In our case, this approach helped new students working on a large academic C++ project understand the structure and components of a complex system.

If you’ve encountered similar issues or found a better solution — feel free to share your thoughts in the comments.

Top comments (0)