Doxygen — C++ Documentation Generator
Doxygen for C++ — comment syntax, Doxyfile configuration, generating HTML/PDF docs, Graphviz call graphs, and CMake integration.
TL;DR
Doxygen generates HTML, PDF, and XML documentation from annotated C++ source comments. Write /// or /** */ comments above declarations; run doxygen to produce browsable docs.
Comment Syntax
/// @brief Single-line brief description.
/// @param x The input value — must be positive.
/// @return The square root, or -1.0 on error.
/// @throws std::invalid_argument if x is negative.
double safe_sqrt(double x);
/**
* @brief Multi-line Doxygen comment.
*
* Longer description goes here. Can span multiple paragraphs.
*
* @tparam T Element type — must satisfy std::totally_ordered.
* @param data Input range to process.
* @param threshold Minimum value to include.
* @return Number of elements above threshold.
*
* @see filter_below()
* @since C++17
*/
template<std::ranges::range R>
size_t count_above(R&& data, std::ranges::range_value_t<R> threshold);
/// @note Not thread-safe. Use external locking if shared across threads.
/// @warning Calling with an empty container is undefined behavior.Documenting Classes
/**
* @class EventBus
* @brief Decoupled publish/subscribe event dispatcher.
*
* Connects publishers and subscribers without direct coupling.
* All operations are thread-safe.
*
* @code
* EventBus bus;
* auto token = bus.subscribe<ClickEvent>([](const ClickEvent& e) {
* std::println("clicked at {},{}", e.x, e.y);
* });
* bus.publish(ClickEvent{10, 20});
* @endcode
*/
class EventBus {
public:
/// @brief Subscribe to events of type T.
/// @tparam T Event type — must be copyable.
/// @param handler Callback invoked on each published event.
/// @return Subscription token; cancel by destroying it.
template<typename T>
[[nodiscard]] Token subscribe(std::function<void(const T&)> handler);
/// @brief Publish an event to all subscribers.
/// @tparam T Event type.
/// @param event The event to publish.
template<typename T>
void publish(T event);
};Doxyfile Quick Setup
# Generate default Doxyfile
doxygen -g
# Key settings to edit in Doxyfile:PROJECT_NAME = "MyProject"
PROJECT_VERSION = "1.0"
INPUT = src/ include/
RECURSIVE = YES
OUTPUT_DIRECTORY = docs/
# Output formats
GENERATE_HTML = YES
GENERATE_LATEX = NO # set YES for PDF
# Diagrams (requires Graphviz dot)
HAVE_DOT = YES
CALL_GRAPH = YES
CALLER_GRAPH = YES
CLASS_DIAGRAMS = YES
INCLUDE_GRAPH = YES
# Extract everything (including undocumented)
EXTRACT_ALL = NO # NO = only documented, YES = everything
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = YES
# Source browser
SOURCE_BROWSER = YES
INLINE_SOURCES = NO# Run
doxygen Doxyfile
# Open docs/html/index.htmlCMake Integration
find_package(Doxygen REQUIRED)
set(DOXYGEN_PROJECT_NAME "${PROJECT_NAME}")
set(DOXYGEN_PROJECT_VERSION "${PROJECT_VERSION}")
set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/docs")
set(DOXYGEN_RECURSIVE YES)
set(DOXYGEN_EXTRACT_ALL NO)
set(DOXYGEN_HAVE_DOT YES)
set(DOXYGEN_CALL_GRAPH YES)
set(DOXYGEN_GENERATE_HTML YES)
set(DOXYGEN_GENERATE_LATEX NO)
doxygen_add_docs(
docs # target name: cmake --build . --target docs
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
COMMENT "Generating API documentation"
)Markdown Pages
<!-- mainpage.md -->
# MyProject {#mainpage}
Welcome to **MyProject** documentation.
## Quick Start
...# Doxyfile
USE_MDFILE_AS_MAINPAGE = mainpage.md
INPUT += mainpage.mdCommon Tags Reference
@brief Short one-line description
@details Extended description
@param name Function parameter
@tparam T Template parameter
@return Return value description
@returns Alias for @return
@throws type Exception that may be thrown
@pre Precondition
@post Postcondition
@note Additional note
@warning Important warning
@see other_fn Cross-reference
@since version When introduced
@deprecated Mark as deprecated
@code / @endcode Inline code example
@todo Outstanding work item
@bug Known bugGroups and Modules
Groups let you organize your public API into named sections that map to separate pages in the HTML output. Without them, all symbols appear in a flat list sorted by file; with groups, users land on "Math utilities" or "I/O utilities" pages that reflect the design of your library.
Define a group once with @defgroup, then pull any symbol into it with @ingroup:
/**
* @defgroup math_utils Math Utilities
* @brief Fast scalar and vector math helpers.
*/
/**
* @ingroup math_utils
* @brief Clamp x to [lo, hi].
*/
template<typename T>
T clamp(T x, T lo, T hi);Use the @addtogroup block syntax with @{ / @} to add multiple consecutive declarations to a group without repeating @ingroup on every one:
/// @addtogroup math_utils
/// @{
/// @brief Linearly interpolate between a and b by factor t.
float lerp(float a, float b, float t);
/// @brief Compute the next power of two >= n.
uint32_t next_pow2(uint32_t n);
/// @}Groups can also be nested — define a child group with @ingroup <parent> in its @defgroup comment. The HTML sidebar renders them as a collapsible tree.
/**
* @defgroup io_utils I/O Utilities
* @brief Stream and file helpers.
*/
/**
* @defgroup io_binary Binary I/O
* @ingroup io_utils
* @brief Low-level binary read/write helpers.
*/Custom HTML and Theming
Doxygen's default HTML theme is functional but dated; a single CSS override or the Doxygen Awesome theme transforms it into a modern, dark-mode-capable reference site.
Control the color scheme with HTML_COLORSTYLE and inject a stylesheet for fine-grained control:
# Doxyfile
HTML_COLORSTYLE = AUTO # LIGHT | DARK | AUTO (follows OS preference)
HTML_EXTRA_STYLESHEET = docs/custom.css
HTML_HEADER = docs/header.html # optional — full custom header
HTML_FOOTER = docs/footer.html # optional — full custom footerFor a production-quality look, use Doxygen Awesome (github.com/jothepro/doxygen-awesome-css). Download the CSS files alongside your Doxyfile and point Doxygen at them:
# one-time setup
git submodule add https://github.com/jothepro/doxygen-awesome-css docs/doxygen-awesome# Doxyfile additions for Doxygen Awesome
GENERATE_TREEVIEW = YES # required by the theme
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_COLORSTYLE = LIGHT # theme handles dark mode via JS
HTML_EXTRA_STYLESHEET = docs/doxygen-awesome/doxygen-awesome.css \
docs/doxygen-awesome/doxygen-awesome-sidebar-only.css
# Optional interactive extensions (copy JS files from the repo):
HTML_EXTRA_FILES = docs/doxygen-awesome/doxygen-awesome-fragment-copy-button.js \
docs/doxygen-awesome/doxygen-awesome-interactive-toc.jsAdd the extension scripts in a custom HTML_HEADER so they initialize before page render. The Doxygen Awesome repo ships a ready-made header.html template — copy it and adjust your project name and logo path.
Generating XML for Custom Doc Pipelines
Doxygen's XML output is the bridge to Sphinx and Read the Docs — enable it to feed structured API data into any downstream documentation tool.
# Doxyfile
GENERATE_XML = YES
XML_OUTPUT = xml/
XML_PROGRAMLISTING = YES # embed source listings in the XML
GENERATE_HTML = NO # optional: skip HTML when only XML is neededRunning doxygen now produces xml/index.xml plus one XML file per symbol. The most common consumer is Breathe, a Sphinx plugin that renders Doxygen XML as RST directives.
Install and configure Breathe in your Sphinx project:
pip install breathe# docs/conf.py
extensions = ["breathe"]
breathe_projects = {
"myproject": "../xml/", # path to Doxygen XML_OUTPUT directory
}
breathe_default_project = "myproject"Then reference C++ symbols directly in RST:
.. doxygenclass:: EventBus
:members:
:undoc-members:
.. doxygenfunction:: safe_sqrt
.. doxygengroup:: math_utils
:members:This workflow is the standard approach for C++ libraries hosted on Read the Docs: Doxygen extracts the API, Breathe bridges the formats, and Sphinx renders the final site with full cross-referencing between narrative docs and API reference.
Documenting Concepts and Ranges (C++20)
Doxygen 1.9.6+ understands C++20 concept declarations natively — earlier versions need a workaround.
On a current Doxygen release, document a concept the same way you document any declaration; the @concept tag is accepted for explicit labeling:
/**
* @concept Sortable
* @brief A type that supports weak ordering and swapping.
*
* @tparam T The type to check.
*
* Satisfied by any type for which `std::sort` is well-formed on a range of T.
*/
template<typename T>
concept Sortable = std::totally_ordered<T> && std::swappable<T>;Document requires-clauses on constrained functions with @satisfies to cross-reference which concept a parameter must model:
/**
* @brief Sort elements in-place using introsort.
* @tparam Iter Iterator type.
* @satisfies Iter must model std::random_access_iterator with a Sortable value type.
* @param first Begin iterator.
* @param last End iterator.
*/
template<std::random_access_iterator Iter>
requires Sortable<std::iter_value_t<Iter>>
void fast_sort(Iter first, Iter last);For Doxygen versions older than 1.9.6 that do not parse concept correctly, use @par to document constraints as a named section:
/**
* @brief Count elements satisfying a predicate.
* @tparam R Input range.
* @tparam P Predicate type.
*
* @par Requirements
* - `R` must satisfy `std::ranges::input_range`.
* - `P` must be invocable with `std::ranges::range_value_t<R>` and return `bool`.
*/
template<std::ranges::input_range R, typename P>
size_t count_if(R&& range, P pred);Collaborative Documentation — Aliases
Project-wide aliases in the Doxyfile let a team enforce consistent documentation vocabulary without repeating boilerplate in every comment.
Add ALIASES entries to the Doxyfile — values may include Doxygen tags, HTML, and ^^ for a forced newline inside a @par block:
# Doxyfile — shared aliases snippet (commit alongside the Doxyfile)
ALIASES = "complexity=@par Time complexity^^"
ALIASES += "thread_safe=@note Thread-safe. May be called from multiple threads simultaneously."
ALIASES += "not_thread_safe=@warning Not thread-safe. Protect with an external mutex when shared."
ALIASES += "since_cpp20=@since C++20 — requires \c -std=c++20 or later."
ALIASES += "nodiscard_reason{1}=@note Return value should not be discarded: \1"With these aliases, any team member can write terse, consistent annotations:
/// @brief Multiply two matrices.
/// @complexity O(n³) naive; use BLAS for large n.
/// @thread_safe
/// @nodiscard_reason{The product is the only output — ignoring it is almost always a bug.}
[[nodiscard]] Matrix matmul(const Matrix& a, const Matrix& b);Keep aliases in a separate file (e.g., docs/aliases.dox) and include it with @INCLUDE = docs/aliases.dox so individual Doxyfiles across a monorepo can share the same vocabulary without copying.
Automated API Docs in CI — GitHub Pages
A GitHub Actions workflow can regenerate and publish your API docs on every push to main with zero manual steps.
The workflow has three stages: install Doxygen, run it, then deploy docs/html to GitHub Pages using the official Actions:
# .github/workflows/docs.yml
name: API Docs
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive # needed if doxygen-awesome is a submodule
- name: Install Doxygen + Graphviz
run: sudo apt-get install -y doxygen graphviz
- name: Cache Doxygen output
uses: actions/cache@v4
with:
path: docs/html
key: doxygen-${{ hashFiles('Doxyfile', 'include/**', 'src/**') }}
- name: Generate docs
run: doxygen Doxyfile
env:
# Treat every Doxygen warning as a build error
DOXYGEN_WARN_AS_ERROR: YES
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/html
deploy-docs:
needs: build-docs
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v4Set WARN_AS_ERROR = YES (or DOXYGEN_WARN_AS_ERROR in the environment) so undocumented parameters and broken @see references surface as CI failures rather than silent omissions. Enable Pages in your repository settings under Settings → Pages → Source → GitHub Actions before the first run.
See Also
- CMake — build system integration; use
doxygen_add_docs()to wire docs into your CMake targets - clang-format — keep source code style consistent so Doxygen's source browser renders cleanly