<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitry Lushchan</title>
    <description>The latest articles on DEV Community by Dmitry Lushchan (@dmitry_lushchan).</description>
    <link>https://dev.to/dmitry_lushchan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3165790%2F8c9b81c6-ff88-456a-8bbb-d903d44c008e.png</url>
      <title>DEV Community: Dmitry Lushchan</title>
      <link>https://dev.to/dmitry_lushchan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dmitry_lushchan"/>
    <language>en</language>
    <item>
      <title>Compiling Header Files — or How to Get Doxygen Documentation for Free</title>
      <dc:creator>Dmitry Lushchan</dc:creator>
      <pubDate>Thu, 29 May 2025 11:10:34 +0000</pubDate>
      <link>https://dev.to/dmitry_lushchan/compiling-header-files-or-how-to-get-doxygen-documentation-for-free-1pho</link>
      <guid>https://dev.to/dmitry_lushchan/compiling-header-files-or-how-to-get-doxygen-documentation-for-free-1pho</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally written in 2012 and translated in 2025 with minor updates for clarity and relevance.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Who This Article Is For
&lt;/h2&gt;

&lt;p&gt;If you're an experienced C++ developer, you might already know the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; c++ &lt;span class="nt"&gt;-I&lt;/span&gt; ./ &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;C++ programming often starts not with writing code, but with reading and understanding someone else's.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

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

&lt;p&gt;After several weeks of cleaning, refactoring, and commenting, I expected Doxygen to produce beautiful documentation. But instead…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;Project/doc
doxygen project-doxyfile
&lt;span class="nb"&gt;cd &lt;/span&gt;html/
./index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...the result was underwhelming: broken diagrams, empty namespaces, and macros misinterpreted as functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Hypothesis: Misconfigured PREDEFINED Option?
&lt;/h2&gt;

&lt;p&gt;I checked all the usual Doxygen settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ENABLE_PREPROCESSING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MACRO_EXPANSION&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXPAND_ONLY_PREDEF&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SEARCH_INCLUDES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INCLUDE_PATH&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything looked reasonable.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Deeper Problem: Header File Assumptions
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

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

&lt;p&gt;This leads to a critical insight:&lt;/p&gt;

&lt;p&gt;A header file that compiles fine in context may still be invalid in isolation — and that's what breaks Doxygen.&lt;/p&gt;

&lt;p&gt;What was wrong with our headers? They were missing explicit &lt;code&gt;#include&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Make Header Files Compile
&lt;/h2&gt;

&lt;p&gt;Our solution was simple and effective:&lt;/p&gt;

&lt;p&gt;Treat every header file as a standalone translation unit and try compiling it.&lt;/p&gt;

&lt;p&gt;That way, any missing includes or broken declarations are caught — and once fixed, Doxygen has no excuse to fail.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;g++ &lt;span class="nt"&gt;-I&lt;/span&gt; ./ &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, to avoid linking errors and specify the language, we added two more flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; c++ &lt;span class="nt"&gt;-I&lt;/span&gt; ./ &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command performs a syntax check (without linking) on all header files.&lt;/p&gt;

&lt;p&gt;We skipped &lt;code&gt;*.inl&lt;/code&gt; files in this step, as they’re not meant to be compiled on their own.&lt;/p&gt;

&lt;p&gt;Once compilation errors were resolved, Doxygen started producing accurate diagrams and documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional Improvements
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Why is this article called "...or How to Get Documentation for Free"?&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;If you’ve encountered similar issues or found a better solution — feel free to share your thoughts in the comments.&lt;/p&gt;

</description>
      <category>doxygen</category>
      <category>documentation</category>
      <category>cpp</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
