Doxygen XLinks
by
V: 2511R0
Website: doxygen
Loading...
Searching...
No Matches
DoxygenXLinks User's Manual

Table of Contents

1. Introduction

1.1 What DoxygenXLinks Provides

Note
This manual is written for the reader who is already familiar with Doxygen and the concept of cross-references.
We have written and tested this software with Doxygen versions
  • 1.14,
  • 1.15,
  • 1.16, and
  • 1.16.1

Doxygen is the de-facto standard for C++ code documentation. It is a fantastic tool that has served developers across many programming languages for decades. Its ability to automatically extract structure from code and generate rich, navigable documentation is unparalleled.

This tool, DoxygenXLinks, is a post-processor for Doxygen that provides two fundamental features:

  1. Equip all HTML-anchors with several different CSS classes.
    This allows a more meaningful design of your documentation. For example, visually separate links that target a class from links to functions, namespaces, or preprocessor macros. And also separate such links to code from links to other documentation anchors.
  2. Provide a simple and intuitive syntax to link to any code entity in your project.
    While here, the main goal was simplicity, one maybe even more important side effect is, that the links get some more semantic meaning: With code refactorings, the link target, as well as the display-text of the link even change "automatically" - or you'll get explicit errors and warnings, with lots of information that helps to quickly fix them.

It is important to say, that the first point of beautifying the HTML output is 100% independent of using the new link syntax that this tool proposes. If you want to stay with the Doxygen \ref-commands, just use DoxygenXLinks as a regular post-processor. All your links will shine in accordance to your css-stylesheet.

Besides these two features, some more candy was added — for example — DoxygenXLinks comes with over 50 built-in expression-functions that you can use to query your project's links. While you might think that this is never needed, you will see that it is very useful for large projects. All about this and more is found in the later chapter 4. Additional Features.

1.2 Feature List

Before we dive into the details of DoxygenXLinks, let's take a look at the features it offers:

  • Equip all HTML-anchors that Doxygen inserts into HTML files with several different CSS classes.
  • Simple usage: DoxygenXLinks parses your Doxyfile and does not need further configuration to run.
  • Inclusion of external Doxygen tag-files.
  • Minimalistic Links: Provide just enough information to identify the target of your next
    cross reference. DoxygenXLinks handles the resolution and generates the appropriate display name automatically.
  • Supports short "scope-hints" (the bare minimum scope context information) to disambiguate an XLink.
  • "Did you mean...?" style proposals with ambiguous links and erroneous (unresolved) links.
  • Automatic display creation from the identifier with simple tweaks and reasonable generation rules.
  • Support for resolving template types and template specializations (including both at once!) by giving short hints.
  • Support for resolving function overloads by giving short hints.
  • The function overload-hell is mitigated by providing copy/paste-friendly suggestions in the tools' output.
  • Context-dependent and -independent linking (leading to relocatable documentation).
  • Intelligent sorting of errors and warnings to enable fast and intuitive work.
  • Changeable output format to make erroneous link-locations clickable in any IDE. (todo)
  • Optional exclamation files, defining source locations to suppress XLinks.
Note
This version of DoxygenXLinks only supports the C++ language. However, because Doxygen uses a consistent XML structure for its tag-files across all supported languages (Java, Python, C#, etc.), adopting DoxygenXLinks for other languages should be quite straightforward. The core logic of "Global Search" and "scope-hints" remains the same.
The community is invited to contribute to the project on GitHub to add support for other languages!

1.3 Performance

DoxygenXLinks will not slow down your build process. Being built using the ALib C++ Framework , this tool reaches some maximum performance. Here is a current statistics output of running DoxygenXLinks on the ALib documentation:

                 |  Sources |    HTML  | Unique/Total 
  ---------------|----------|----------|--------------
  XLinks         |   11,241 |   11,517 |    4,207
      Unresolved |        0 |        0 |        0
       Ambiguous |        0 |        0 |        0
       Erroneous |        0 |        0 |        0
        Warnings |        0 |        0 |        0
  EL-Anchors     |      -/- |   77,596 |   77,596
      Unresolved |      -/- |        0 |        0
  ELREF-Anchors  |      -/- |        0 |        0
      Unresolved |      -/- |        0 |        0
  ---------------|----------|----------|--------------
           Files |      511 |    1,697 |    2,208
           Lines |  154,424 |  486,697 |  641,121
            Size |   7.2MiB |  32.7MiB |  39.9MiB
            Time |   034 ms |   028 ms |   062 ms

Total Time: 071 ms

It takes DoxygenXLinks just around 70 milliseconds to replace almost 90,000 anchors in 1.700 HTML-files!

To post-process the documenatation of the DoxygenXLinks project itself, around 7,500 links are replaced in just 15 milliseconds. This includes reading and indexing the tag-file of ALib that comes with approximately 8,500 documented entities.

Note
Besides the use of ALib, this speed is achieved because of the machine's file-cache: Remember that DoxygenXLinks is a post-processor of Doxygen. With that, the sources have just been read and the HTML files just been written by Doxygen. This way, disk i/o is negligible in this case.

2. CSS Link Styles for HTML Output

2.1 Introduction

While the effort and time of writing this tool was invested mainly to provide the new link syntax, let us start with this chapter about how DoxygenXLinks will add some CSS classes to all anchors of the Doxygen HTML output.
For this, you do not need to modify your current documentation sources! Instead, all you have to do is two things:

  1. Integrate DoxygenXLinks into your build process.
    This is an easy effort, because DoxygenXLinks reads the Doxygen's INI-file and grabs all information it needs from there.
    See chapter 5. Usage (Calling The Tool) for details.
  2. If you do not already have one, create an additional CSS-stylesheet and provide styles for the DoxygenXLinks-classes.
    Note that adding a stylesheet is already a standard-feature of Doxygen.
    The way how to do this is documented here: Doxygen's CSS-Styling

Doxygen itself exposes only two separate CSS-classes for links:

  1. Class "el" for links into local file and anchors. Local here meansk, those that link to any code-entity or to any other piece of documentation of the project you actually compile with Doxygen.
  2. Class "elRef" for links into external documentation.
    External documentation is documentation of projects that you import into your current project by using Doxygen Tagfiles .

This does not provide much flexibility for styling links.

Therefore DoxygenXLinks searches all HTML-anchors of the Doxygen output, analyses what sort of target the anchor points to, and adds additional CSS classes to them. With that, the following styles are possible:

Link Target Type Internal Links External Links
Namespace dxl expressions
Namespace Function ConvertHTMLEntitiesToAscii ScanFiles
Namespace Variable FindInheritedMembers GLOBAL_ALLOCATOR
Union dxl::DXLTestUnion boxing::Placeholder
Struct AnchorKey detail::VirtualMachine
Class dxl::XLink expressions::Compiler
Member Function XLink::Parse TString::IndexOf
Member Variable XLink::IsLocal TString::length
Enumeration Kinds Verbosity
Enum. Element Target::EnumElement Verbosity::Info
Type definition ChainedAString alib::String
Resolved definition ChainedAString alib::String
Preprocessor macro TEST_CONSTANT ALIB_ENUMS_ASSIGN_RECORD
Source folder src alib/enumops
Source file styles.hpp alib/enumops/bitwise.inl
Page/Group/Anchor 2.2 CSS Styles Added By DoxygenXLinks 2.3 Tabular List Of Modules
Class with Display a key to the anchor a small vm
Variable with Display the display string the length of a string

Note that the colors and fonts above are just examples. We have styled external links here only in one different way: They are always underlined, while internal links are underlined only when hovered with the mouse.

Used in a text, this looks as follows: Once upon a time, someone wrote a small VirtualMachine which runs programs that evaluate runtime expressions in C++ software. The programs are compiled by method Compile of class Compiler. To enable the users of the library to easily add custom expression semantics, the macro CALCULUS_CALLBACK is provided. This is used to add entries to the field Calculus::Operators.

2.2 CSS Styles Added By DoxygenXLinks

DoxygenXLinks keeps the original CSS-class (either "el" or "elRef") on anchors and adds multiple new classes.

First, for each target type an own class is added add follows:

Link Target Type CSS Style Class Added
Pages, Groups and Doc-Anchors xl-doc
Folders xl-dir
Files xl-file
Source Files xl-srcfile
Line Numbers in Source Files xl-srcline
Code Entities (all of the below) xl-entity
Macros xl-macro
Type definition / using-statement xl-typedef
Concept xl-concept
Namespace xl-ns
Struct xl-struct
Class xl-class
Union xl-union
Variable xl-var
Function xl-func
Enumeration xl-enum
Enumeration Element (Value) xl-enumelem

Next, for certain groups of targets, additional classes are added. This allows to style the whole group of targets in one go. The styles are:

Link Target Type Styles
Folders, files, source files and links line numbers in source files. xl-filedir
Classes, Structs, and Unions xl-record
Template Types xl-template
Template Specializations xl-tempspec
Links targeting inherited members and members linked through a type-definitions. xl-indirect
Variables, Functions and Enumerations located in a namespace xl-in-ns
Variables, Functions and Enumerations located in a record (Members/fields/methods) xl-in-rec
Entities that have a custom display text xl-display
Note
  • Templated template specializations get both, the "xl-template" and the "xl-tempspec" class. As far as we know, such a combination can hardly be addressed with the \ref-command and in most cases needs the XLink syntax.
  • The style xl-indirect is only applied when using the XLink syntax, because DoxygenXLinks cannot detect if the originating \ref-command used indirect targeting.

Finally, all anchors that have been found that are not using the XLink syntax, which are anchors that either use the \ref-syntax or those that have been inserted by Doxygen automatically, are equipped with "xl-el". However, this is just for completeness. There should be now viewable difference between the two link styles and automatically inserted anchors.

If the replacement of anchors that do not use the XLink syntax fails with a link (for whatever reason), the class "xl-elukn" is attached. Such "unknown" anchors might be styled in a strong color so that they are quickly noticed. When noticed, the verbosity of this tool might be increased to understand the problem.

2.3 A Sample Stylesheet

As a sample, the stylesheet coming with the source code of DoxygenXLinks could be used. (This sample results in the colors and styles you just see in this manual!)

The most relevant excerpt of that sheet is shown below and can be copied as a jump-start for your own stylesheet:

/* XL doc */
a.el.xl-doc , a.elRef.xl-doc { color: #9E2544; font-family: "AWorxSerif"; font-style: italic; }
/* XL files/dirs */
a.el.xl-filedir , a.elRef.xl-filedir { color: #396c0b; }
a.el.xl-dir , a.elRef.xl-dir { font-family: "AWorxSansBold"; }
a.el.xl-file , a.elRef.xl-file { font-family: "AWorxSans"; }
/* XL entities */
a.el.xl-entity , a.elRef.xl-entity { color: deeppink; font-family: "AWorxCode"; }
a.el.xl-ns , a.elRef.xl-ns { color: #0531b6; }
a.el.xl-record , a.elRef.xl-record { font-weight: bold; }
a.el.xl-union , a.elRef.xl-union { color: chocolate; }
a.el.xl-class , a.elRef.xl-class { color: darkblue; }
a.el.xl-struct , a.elRef.xl-struct { color: darkslateblue; }
a.el.xl-enum , a.elRef.xl-enum { color: crimson; font-weight: bold; }
a.el.xl-enumelem , a.elRef.xl-enumelem { color: crimson; font-weight: bold; font-style: italic; }
a.el.xl-typedef , a.elRef.xl-typedef { color: darkblue;font-weight: bold; font-style: italic; }
a.el.xl-func , a.elRef.xl-func { color: #360f88; }
a.el.xl-var , a.elRef.xl-var { color: chocolate; }
a.el.xl-concept , a.elRef.xl-concept { font-family: "AWorxCode"; font-weight: bold; }
a.el.xl-macro , a.elRef.xl-macro { color: goldenrod; }
/* XL variables and functions in namespace get colored like namespaces, but bold */
a.el.xl-var.xl-in-ns , a.elRef.xl-var.xl-in-ns { color: #0531b6; font-weight: bold; }
a.el.xl-func.xl-in-ns , a.elRef.xl-func.xl-in-ns { color: #0531b6; font-weight: bold; }
a.el.xl-indirect , a.elRef.xl-indirect { font-style: italic; }
/* If a display text is given, then the font goes back to the main documentation font */
a.el.xl-display , a.elRef.xl-display { font-family: "AWorxSerif"; }

When you start using the (main!) feature of DoxygenXLinks, which is explained in the next chapter, you might also want to insert the following CSS-ruleset:

/* XL doc */
.xl-doc , .xl-doc { color: #022E34; font-family: "AWorxSerif"; font-style: italic; }
/* XL files/dirs */
.xl-filedir , .xl-filedir { color: #022E34; }
.xl-dir , .xl-dir { font-family: "AWorxSansBold"; }
.xl-file , .xl-file { font-family: "AWorxSans"; }
/* XL entities */
.xl-entity , .xl-entity { color: #022E34; font-family: "AWorxCode"; }
.xl-ns , .xl-ns { color: #022E34; }
.xl-record , .xl-record { font-weight: bold; }
.xl-union , .xl-union { color: #022E34; }
.xl-class , .xl-class { color: #022E34; }
.xl-struct , .xl-struct { color: #022E34; }
.xl-enum , .xl-enum { color: #022E34; font-weight: bold; }
.xl-enumelem , .xl-enumelem { color: #022E34; font-weight: bold; font-style: italic; }
.xl-typedef , .xl-typedef { color: #022E34; font-weight: bold; font-style: italic; }
.xl-func , .xl-func { color: #022E34 }
.xl-var , .xl-var { color: #022E34; }
.xl-concept , .xl-concept { color: #022E34; font-family: "AWorxCode"; font-weight: bold; }
.xl-macro , .xl-macro { color: #022E34; }
/* XL variables and functions in namespace get colored like namespaces, but bold */
.xl-var.xl-in-ns , .xl-var.xl-in-ns { color: #022E34; font-weight: bold; }
.xl-func.xl-in-ns , .xl-func.xl-in-ns { color: #022E34; font-weight: bold; }
.xl-indirect , .xl-indirect { font-style: italic; }
/* If a display text is given, then the font goes back to the main documentation font.
But in-fact, with %-links this should not be used at all. */
.xl-display , .xl-display { font-family: "AWorxSerif"; }

3. XLinks

3.1 How Linking Works With Doxygen

We, the makers of the ALib C++ Framework , have been using Doxygen since our project's start, and are proud to not only have documented every single code entity, but also to provide a set of detailed Programmer's Manuals, also written with Doxygen.
To create the ALib documentation (as of January 2026) Doxygen processes 511 source files and generates documentation for 8,558 code entities and anchors. And yes, there we have manually (!) placed 11,241 cross-references!

DoxygenXLinks is designed to place cross-references as effortlessly as possible. It acts as a post-processor for Doxygen, allowing you to use a simple, flexible, and powerful link syntax.

3.1.1 Setting A Doxygen Link

With Doxygen, you can link to any code entity in your project using a simple syntax:

    \ref my::namespace::Type ["Link Display"]

Here is a sample, linking to a class in the ALib C++ Framework:

    \ref alib::expressions::detail::VirtualMachine.

The result of this link looks like this: alib::expressions::detail::VirtualMachine.

As you see, the link text is automatically generated from the identifier and includes the namespace scope. While this is a great start and straightforward to use, in most cases (namely when the context is clear) the namespace scope should be suppressed in the output link.
To do this, Doxygen allows you to add a display text to the link:

    \ref alib::expressions::detail::VirtualMachine "VirtualMachine"

The result of this link now is: VirtualMachine

Well, not a problem you might say, but look at the numbers of our project we gave above. We have more than eleven-thousand cross-references. This is quite a lot of typing. And it is error-prone: When you change the name of the class, the display text likewise has to be changed. If you miss that, your documentation is erroneous.

Therefore, we decided to create this tool - DoxygenXLinks - to make linking for us as effortless and safe as possible.

The Doxygen link above, written as an "XLink" simply looks like this:

    #"VirtualMachine"

From 63 characters, only 17 remained. The result is a 100% identical HTML output: VirtualMachine

While shortening the links and reducing documentation errors remained the main goal, during development some more candy was added.

3.1.2 Doxygen's Autolink Feature

Doxygen provides a feature called Autolink that allows you to link to identifiers in your source code without having to write the full scope-path to the entity.

While this is great for small projects, the drawbacks overweight the advantages when projects grow in size:

  • If an entity name is very generic, using the word in other contexts starts linking to the entity without the user (documentation writer) knowing it.
  • Even if the link is rightful, repetitive linking to the same entity within the same paragraph, overloads a text with anchors. Usually only the first occurrence should be linked. Therefore, a user has to start prepending the entity name with '%'-markers to suppress the auto-link.
  • If the name of the code entity is changed (refactoring), then the author of the documentation is not noticed to update his documentation. Doxygen's autolink-feature will just not set anchors for the entity now wrongly spelled in the documentation!
  • Symbols that are all lower case are not linked at all. This is probably the case because of the first bullet point above.

This and other considerations (i.e that the link display text is not changeable) renders the Autolink-feature unsuitable for larger projects.

3.2 The XLink Syntax"

The XLinks processed by this tool are placed within your documentation using a special syntax that always starts with a hash character # followed by the actual link definition in quotes '"'.

The formal definition of an XLink is:

XLink           = '#"' ['%'] ['^'] [tspec ';']  + target + [';' + display] + '"'
tspec           = D | F | P | N | O | T | R | C | S | U | E | A | M | V

target          = [template] [scopeHints] + [scope] + name + [args | subscript | specialization]
template        = ['template '] + '<' + paramHints + '>'
scopeHints      = { substring + ' ' }
scope           = { identifier + '::' }
name            = identifier
args            = '(' + [paramHints] + ')'
subscript       = '[' + index + ']'
specialization  = '<' + paramHints + '>'
paramHints      = hint + { ',' + hint }

display         = [displayHint] [displayHint...] ['/']  [text]
displayHint     = digit | '*' | '?' | '!' | '(' | ')'| '<' | '>' | '[' | ']'

While this formal definition looks complicated, you will see that the syntax is very simple and intuitive.

By walking it through along some examples, it will quickly become clearer.

3.3 The XLink Target"

3.3.1 Parents and Scope-Hints"

In the introduction we looked at the sample XLink:

#"VirtualMachine"  // equivalent to \ref alib::expressions::detail::VirtualMachine "VirtualMachine"

Let's choose a different example: We want to link to an enumeration called "Exceptions". So we try:

#"Exceptions"  

The output of DoxygenXLinks is:

Ambiguous XLink #"Exceptions".
  Could be (E) enumeration alib::cli::Exceptions          tag:  /tmp/alib.doxygen.tag:47314 ->:  file:///tmp/alib_html//namespacealib_1_1cli.html#aa4e23a276bcf38b2fc6aefdf9230740d 
  Could be (E) enumeration alib::variables::Exceptions    tag:  /tmp/alib.doxygen.tag:49968 ->:  file:///tmp/alib_html//namespacealib_1_1variables.html#aafab507a0918ad481dc2dd39cf8f944e 
  Could be (E) enumeration alib::expressions::Exceptions  tag:  /tmp/alib.doxygen.tag:48200 ->:  file:///tmp/alib_html//namespacealib_1_1expressions.html#aacdc6376356d57d42271eb7a052cd6ea 
  Could be (E) enumeration alib::app::App::Exceptions     tag:  /tmp/alib.doxygen.tag:7025  ->:  file:///tmp/alib_html//classalib_1_1app_1_1App.html#a841b9bb164c899d4d25d5845e632c8e9 
  Could be (E) enumeration dxl::Exceptions                tag:  /tmp/doxygenxlinks.tag:3599 ->:  file:///tmp/doxygenxlinks.html/namespacedxl.html#a0b9f32eec8f6e6c66cb7bc12bb55e262 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:425:5

The output tells us that DoxygenXLinks found five possible matches for the identifier "Exceptions". All are found in different namespaces. The first four are from the ALib library. The last one is from this tool's own source code (which we are documenting here).

Note
The output tells us also:
  • the tag-file where the entity was found, and
  • the target HTML-URL including the file and a potential anchor.
This allows - in case your IDE or console supports it - to directly open the target documentation or even the doxygen tag-file entry to do further analysis of the ambiguity.

To resolve the ambiguity, in that case we have two options:

1. Using Parents
We add parents using the C++ syntax "::" to narrow down the search.
With that the link could be resolved, for example, with:
#"cli::Exceptions"       --> Exceptions  (links to alib::cli::Exceptions)       
#"alib::cli::Exceptions" --> Exceptions  (links to alib::cli::Exceptions)
#"dxl::Exceptions"       --> Exceptions  (links to dxl::Exceptions)       

Besides the namespace, the tag-file name is also part of the path to the entity that is checked. We had seen in the error output that the tag-file names are alib.doxygen.tag and doxygenxlinks.tag.
Thus, these links also work:

#"alib.doxygen.tag::alib::cli::Exceptions" --> Exceptions
#"doxygenxlinks.tag::dxl::Exceptions"      --> Exceptions

Well, that's not very nice. DoxygenXLinks is much smarter:

2. Using "Scope-Hints"
The other option is to use what we call scope-hint to disambiguate the link.
Scope-hints are substrings of the scope to the entity you want to link to. Instead of attaching them with colons, a space is used. Scope-hints do not need to be complete. With them a substring search is performed on the path to the entity. If more than one scope-hint is given they all need to be found as substrings in the order of appearance.
Samples for resolving the link with scope-hints are:
#"xpr Exceptions"       --> Exceptions   (links to alib::expressions::Exceptions)    
#"app app Exceptions"   --> Exceptions   (links to app::App::Exceptions)
#"dxl Exceptions"       --> Exceptions   (links to dxl::Exceptions)    

The last sample shows that scope-hints are not even case-sensitive.
And again, the tag-file can also be used:

#"links.tag Exceptions" --> Exceptions   (links to dxl::Exceptions)
Note
The difference between scope-hints and parents could be phrased as follows: Scope-hints are for the lazy ones! Both techniques can be used to resolve ambiguities. Their effect is otherwise completely the same.
Using scope-hints is more convenient for the writer, but for the reader (of a document source!) it might sometimes be less intuitive, for example, if a scope-hint "ex "
was used, instead of parent "expressions::". A writer could even use the hint "x " - it would be enough to disambiguate the link.

Scope-hints and scopes can be used both in one link, for example:

#"xpr VirtualMachine::Command" --> Command

Of course, the hints have to precede the scopes.

3.3.2 Automatic Disambiguation

We just saw how scope-hints and parent identifiers can be used to disambiguate links. DoxygenXLinks also provides some rules that perform an "automatic disambiguation" for some cases. You will learn about these rules while walking through this manual (or while you are just using this tool).

Note
We do not give a precise formal list and specification of all rules here to keep our manual short. Interested readers are encouraged to read the source code, especially the implementation of the methods DoxygenXLinks::GetXLink and Index::Search

A very obvious example is about constructors: The following link to the main class of this tool

    #"DoxygenXLinks" --> DoxygenXLinks

is not ambiguous, even though there are two code entities with the same name: The class and its constructor.
So one of the rules DoxygenXLinks uses could be phrased:

Rule: If there are two entities with the same name in an inheritance relationship, then the outer entity is chosen.

The automatic disambiguation rules are convenient even in cases where a link is still ambiguous. Let's look at this example:

#"Command"

DoxygenXLinks gives us the following error:

Ambiguous XLink #"Command".
  Could be (S) struct alib::cli::Command                                  tag:  /tmp/alib.doxygen.tag:11967 ->:  file:///tmp/alib_html//structalib_1_1cli_1_1Command.html 
  Could be (C) class  alib::expressions::detail::VirtualMachine::Command  tag:  /tmp/alib.doxygen.tag:12021 ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html 
  Could be (S) struct alib::app::App::StateMachine::Command               tag:  /tmp/alib.doxygen.tag:11941 ->:  file:///tmp/alib_html//structalib_1_1app_1_1App_1_1StateMachine_1_1Command.html 
  Further entities with the same name:                                                                           
     (M) constructor alib::cli::Command::Command(CommandLine*)            tag:  /tmp/alib.doxygen.tag:11990 ->:  file:///tmp/alib_html//structalib_1_1cli_1_1Command.html#ad55fb52f99b104025883d19ef07410e4 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(Program*, const Box&, const String&, integer, integer)    tag:  /tmp/alib.doxygen.tag:12314    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a0c3b702edd9b2b67a1e8f8081154cb4c 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(integer, integer, JumpType)                               tag:  /tmp/alib.doxygen.tag:12307    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a4c6fadc369352333adbdbb7197488b5f 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(const Box&, bool, integer, integer)                       tag:  /tmp/alib.doxygen.tag:12300    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#ac40adad4adf981abd2a67f32a1160f44 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(CallbackDecl, bool, int, const Box&, const String&, bool, integer, integer)    tag:  /tmp/alib.doxygen.tag:12293    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a38e23f1e88245949e7c34b0001b4dac5 

As you see, the output tells us that DoxygenXLinks found three possible matches for the identifier Command and then talks about "further entities". Those are all constructors, which are superseded by their corresponding compound-type. With this separation, it is rather simple to resolve the ambiguity in the (more likely!) case that one of the three types was meant to be linked to.

But also, if a constructor was meant, all necessary information is already given in the error output. In the next chapter we will see how to overcome the automatic rule and instead choose one of those.

3.3.3 Disambiguating By Target Kind"

DoxygenXLinks also provides a way to disambiguate links by target kind. On the following link:

#"String"

the tool tells us:

Ambiguous XLink #"String".
  Could be (T) typedef alib::String                                      tag: /tmp/alib.doxygen.tag:45140 ->:  file:///tmp/alib_html//namespacealib.html#a741b6610debe8ddae80beb3dbd74c53d 
  Could be (A) enumvalue alib::format::FormatterStdImpl::PHTypes::String tag: /tmp/alib.doxygen.tag:19947 ->:  file:///tmp/alib_html//classalib_1_1format_1_1FormatterStdImpl.html#a5ecaa0736152b685865b846b2da88e23a27118326006d3829667a400ad23d5d98 
  Could be (V) member-variable alib::expressions::Types::String          tag: /tmp/alib.doxygen.tag:42077 ->:  file:///tmp/alib_html//structalib_1_1expressions_1_1Types.html#ac98596ea00e1d87dcd7003f8d00aeac3 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:557:5

In this case, the proposals all have a different target kind. Prepending a "kind specification character", along with a semicolon disambiguates the link:

#"T;String" -> String  (links to alib::String) 
#"A;String" -> String  (links to alib::format::FormatterStdImpl::PHTypes::String)  
#"V;String" -> String  (links to alib::expressions::Types::String)  

The following table shows the kind specification characters:

Character Kind
'D' Directory
'F' File
'P' Macro (Preprocessor)
'N' Namespace
'O' Concept
'T' Type definition
'R' Record (class, struct, or union)
'C' Class
'S' Struct
'U' Union
'E' Enumeration
'A' Enumeration element (value)
'M' Namespace-function or method
'V' Namespace- or member-variable
Note
The kind specification characters are case-insensitive.
Attention
: While the specification-characters are used for disambiguation here, when linking to directories and files, giving the propper 'D' or 'F' is mandatory! More on this is explained in the chapter 3.8 Linking To Files And Folders.

3.3.4 Disambiguating Functions "

DoxygenXLinks has two options to disambiguate overloaded functions.

3.3.4.1 By Parameters "

We just go back to the sample output of a previous section:

#"Command"

with the error message:

Ambiguous XLink #"Command".
  Could be (S) struct alib::cli::Command                                  tag:  /tmp/alib.doxygen.tag:11967 ->:  file:///tmp/alib_html//structalib_1_1cli_1_1Command.html 
  Could be (C) class  alib::expressions::detail::VirtualMachine::Command  tag:  /tmp/alib.doxygen.tag:12021 ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html 
  Could be (S) struct alib::app::App::StateMachine::Command               tag:  /tmp/alib.doxygen.tag:11941 ->:  file:///tmp/alib_html//structalib_1_1app_1_1App_1_1StateMachine_1_1Command.html 
  Further entities with the same name:                                                                           
     (M) constructor alib::cli::Command::Command(CommandLine*)            tag:  /tmp/alib.doxygen.tag:11990 ->:  file:///tmp/alib_html//structalib_1_1cli_1_1Command.html#ad55fb52f99b104025883d19ef07410e4 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(Program*, const Box&, const String&, integer, integer)    tag:  /tmp/alib.doxygen.tag:12314    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a0c3b702edd9b2b67a1e8f8081154cb4c 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(integer, integer, JumpType)                               tag:  /tmp/alib.doxygen.tag:12307    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a4c6fadc369352333adbdbb7197488b5f 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(const Box&, bool, integer, integer)                       tag:  /tmp/alib.doxygen.tag:12300    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#ac40adad4adf981abd2a67f32a1160f44 
     (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(CallbackDecl, bool, int, const Box&, const String&, bool, integer, integer)    tag:  /tmp/alib.doxygen.tag:12293    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a38e23f1e88245949e7c34b0001b4dac5 

This time, we want to link to a constructor of the class Command nested in the VirtualMachine.
Instead of adding parents or scope-hints, we just add one (!) of the parameters of the constructor we want to link to:

    #"Command(Program*)"  --> Command  (links to Command(Program*, const Box&, const String&, integer, integer))

As you can see, it is not necessary to add all parameters, and it does not even have to be the first one. The link:

    #"Command(JumpType)"  --> Command  (links to Command(integer, integer, JumpType))

likewise resolves nicely. And, for the real lazy typers of you, even

    #"Command(Jump)" -->Command  (links to Command(integer, integer, JumpType))
    #"Command(J)"    -->Command  (links to Command(integer, integer, JumpType))

is enough.
If you however pass a parameter that exists in more than one overload, DoxygenXLinks will give you an error. The XLink:

    #"Command(bool)"

results in:

Ambiguous XLink #"Command(bool)".
  Could be (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(const Box&, bool, integer, integer)    tag:  /tmp/alib.doxygen.tag:12300 ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#ac40adad4adf981abd2a67f32a1160f44 
  Could be (M) constructor alib::expressions::detail::VirtualMachine::Command::Command(CallbackDecl, bool, int, const Box&, const String&, bool, integer, integer)    tag:  /tmp/alib.doxygen.tag:12293    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html#a38e23f1e88245949e7c34b0001b4dac5 
  Further entities with the same name:
     (S) struct alib::cli::Command                                                                                                                                      tag:  /tmp/alib.doxygen.tag:11967    ->:  file:///tmp/alib_html//structalib_1_1cli_1_1Command.html 
     (C) class  alib::expressions::detail::VirtualMachine::Command                                                                                                      tag:  /tmp/alib.doxygen.tag:12021    ->:  file:///tmp/alib_html//classalib_1_1expressions_1_1detail_1_1VirtualMachine_1_1Command.html 
     (S) struct alib::app::App::StateMachine::Command                                                                                                                   tag:  /tmp/alib.doxygen.tag:11941    ->:  file:///tmp/alib_html//structalib_1_1app_1_1App_1_1StateMachine_1_1Command.html 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:644:9

You can see two things here:

  1. The link is ambiguous, two of the originally five constructors are possible targets.
  2. This time, the types (struct and class) are named "further entities".

This is because the link includes braces, and thus DoxygenXLinks understands that a function was meant.

Now all you have to do is to disambiguate the link by adding a set of parameters that do
not exist in any of the other overloads in the right sequence order, for example:

    #"Command(const String&, bool)"  --> Command  (links to Command(CallbackDecl, bool, int, const Box&, const String&, bool, integer, integer))

If only one overloaded function existed, just adding empty parenthesis would disambiguate the link.

Adding the Parameters to the Display:
Let's look at a final sample: We are adding all parameters of one of the functions:

    #"Command(integer, integer, JumpType)" --> Command(integer, integer, JumpType)

DoxygenXLinks here assumes that the user not only wants to disambiguate the link, but also wants to have the parameters being displayed in the link text - and so it does!
More information about how to automatically add parameters and other tricks for tweaking the display text will be discussed in the later chapter 3.4 The XLink Display Text.

3.3.4.2 By Qualifiers "

In respect to function qualifiers (which allow function overloading), DoxygenXLinks is very pragmatic: If an overload of a function exists that adds a qualifier to the function signature, DoxygenXLinks will resolve the link to the version without the qualifier.

As an example, the link:

    #"TCursor::Value"  -> Value  (links to T & TCursor::Value() )

is not ambiguous, although there are two overloads of this function. The other can be resolved by adding the qualifier const:

    #"TCursor::Value() const"  -> Value() const (links to const T & TCursor::Value() const)

As you can see, if the qualifier is given, also the parameter list is displayed in the link text. This is because it would look strange to an experienced programmer to see a link "Value const" without at least the function braces.

3.3.5 Resolving Template Functions "

As far as we have understood, Doxygen (at the time of writing in version 1.15) does not store information about template parameters of functions in the tag-file. It also seems that internally, template parameters of functions are ignored. We added this test to the source code of this tool:

/// Template 1
/// @param a An A.
/// @tparam T Any type.
template<typename T> void foo(T a) {(void)a;}
/// Template 2
/// @param a An A.
/// @tparam N Type int.
template<int N> void foo(int a) {(void)a;}

While doxygen accepts template parameters in the link, all links go to the same target:

1. \ref dxl::foo                           
2. \ref dxl::foo<N>                     
3. \ref dxl::foo<typename T>   
4. \ref dxl::foo<int N>             
5. \ref dxl::foo<int T>             

The results of the above tests are:

  1. dxl::foo
  2. dxl::foo<N>
  3. dxl::foo<typename T>
  4. dxl::foo<int N>
  5. dxl::foo<int T>

Also, what you see here is that the link text includes the given template parameters, even though they are not considered in the link resolution.

With that, as of today, we do not have a way to disambiguate links to template functions by adding template arguments, and thus DoxygenXLinks does not allow template parameters in function links at all.

Nevertheless, disambiguation is still possible using the function-parameter syntax described above:

#"foo"       <- ambiguous
#"foo(T)"    foo(T)    
#"foo(int)"  foo(int)

3.3.6 Resolving Template Types

3.3.6.1 Base Types

With template types, the situation is better: Doxygen stores the template parameters in the tag-file. DoxygenXLinks can therefore resolve targets that are templates, template specializations, or both correctly.

But let's first look at linking to non-specialized types. Those can be resolved without adding template parameters to the link:

    #"ArrayTraits"  --> ArrayTraits  (links to template <typename TStringSource, typename TChar> characters::ArrayTraits))

This is because an internal disambiguation rule lets DoxygenXLinks prefer the base-template over specializations. Nevertheless, template parameters can be added to the start of the link. To force DoxygenXLinks to search a templated type, you have the following options:

  • Prepend the keyword template,
  • Prepend emtpy angle brackets "< >",
  • Prepend angle brackets containing one or more template parameters, or
  • a blend of the above.

This way, all of the following links are valid:

- #"ArrayTraits"                                                   --> ArrayTraits
- #"template ArrayTraits"                                          --> template <typename TStringSource, typename TChar> ArrayTraits
- #"<>ArrayTraits"                                                 --> template <typename TStringSource, typename TChar> ArrayTraits
- #"template <typename TStringSource, typename TChar> ArrayTraits" --> template <typename TStringSource, typename TChar> ArrayTraits
- #"<typename TStringSource, typename TChar> ArrayTraits"          --> template <typename TStringSource, typename TChar> ArrayTraits
- #"<typename> ArrayTraits"                                        --> template <typename TStringSource, typename TChar> ArrayTraits

The provision of a wrong template parameter will result in an error:

- #"<xyz> ArrayTraits"  <- INVALID LINK

Leads to:

Unresolved XLink #"<xyz> ArrayTraits".
  You probably meant: template (S) struct template <typename TStringSource, typename TChar> alib::characters::ArrayTraits                                            tag:  /tmp/alib.doxygen.tag:8396 ->:  file:///tmp/alib_html//structalib_1_1characters_1_1ArrayTraits.html 
    Or  template (S) specialization template <typename TChar> alib::characters::compatibility::std::ArrayTraits<std::vector< TChar >, TChar>                         tag:  /tmp/alib.doxygen.tag:8466 ->:  file:///tmp/alib_html//structalib_1_1characters_1_1compatibility_1_1std_1_1ArrayTraits_3_01std_1_1vector_3_01TChar_01_4_00_01TChar_01_4.html 
    Or  template (S) specialization template <typename TChar> alib::characters::compatibility::std::ArrayTraits<std::span< const TChar >, TChar>                     tag:  /tmp/alib.doxygen.tag:8461 ->:  file:///tmp/alib_html//structalib_1_1characters_1_1compatibility_1_1std_1_1ArrayTraits_3_01std_1_1span_3_01const_01TChar_01_4_00_01TChar_01_4.html 
    Or  template (S) specialization template <typename TChar> alib::characters::compatibility::std::ArrayTraits<std::basic_string_view< TChar >, TChar>              tag:  /tmp/alib.doxygen.tag:8456 ->:  file:///tmp/alib_html//structalib_1_1characters_1_1compatibility_1_1std_1_1ArrayTraits_3_01std_1_1basic__string__view_3_01TChar_01_4_00_01TChar_01_4.html 
    Or  template (S) specialization template <typename TChar> alib::characters::compatibility::std::ArrayTraits<std::basic_string< TChar >, TChar>                   tag:  /tmp/alib.doxygen.tag:8447 ->:  file:///tmp/alib_html//structalib_1_1characters_1_1compatibility_1_1std_1_1ArrayTraits_3_01std_1_1basic__string_3_01TChar_01_4_00_01TChar_01_4.html 
    Or  template (S) specialization template <typename TChar, size_t TLength> alib::characters::compatibility::std::ArrayTraits<std::array< TChar, TLength >, TChar> tag:  /tmp/alib.doxygen.tag:8437 ->:  file:///tmp/alib_html//structalib_1_1characters_1_1compatibility_1_1std_1_1ArrayTraits_3_01std_1_1array_3_01TChar_00_01TLength_01_4_00_01TChar_01_4.html 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:771:7

Do you remember from the previous section that DoxygenXLinks adds the function parameters to the link text when all parameters were given? With the template arguments it is slightly different: Here the template parameters are added if just one parameter is given (or the keyword template).

That is why the links given above all resolve to the same target, but only the first one does not add the template parameters to the link text.

Note
The rationale for this different treatment is that the provision of parameters with overloaded functions is mandatory for the disambiguation. With template types it is not, they are not "overloaded".
Therefore, the main reason why you might want to add some hint to the XLink is not to disambiguate, but to have the full template syntax displayed in the link text!

3.3.6.2 Template Specializations"

The error output of the sample ArrayTraits in the previous sections shows that template specializations exist for this type. DoxygenXLinks can resolve those targets correctly. To do this, a sufficient subset of the specialized template parameters must be given. Sufficient here means that it has to be enough information to disambiguate the link from the other specializations.

In contrast to targeting the base template, with specializations such parameters have to follow the type name. Here are some examples:

#"ArrayTraits<std::vector< TChar >>"                --> ArrayTraits<std::vector< TChar >, TChar>
#"ArrayTraits<std::span>"                           --> ArrayTraits<std::span< const TChar >, TChar>
#"ArrayTraits<std::array< TChar, TLength >, TChar>" --> ArrayTraits<std::array< TChar, TLength >, TChar>
#"ArrayTraits<std::basic_string< char8_t >>"        --> ArrayTraits<std::basic_string< char8_t >, nchar>

Often specializations are still (or again) templates. To provide the full template syntax, the keyword template can be added to the start of the link:

#"template ArrayTraits<std::span>" --> template <typename TChar> ArrayTraits<std::span< const TChar >, TChar> 

The lazy typers among you can also use just write:

#"<>ArrayTraits<std::s>" --> template <typename TChar> ArrayTraits<std::span< const TChar >, TChar>

3.3.7 Variable Subscripts

Variables can be linked just as function members, for example:

#"Styles::list"  --> list  (links to Styles::list[MAX_STYLES])

There is no further disambiguation needed here.
But this variable is an array type. We are allowed give the subscript with the link:

#"Styles::list[MAX_STYLES]"  --> list[MAX_STYLES]

You probably already guessed that this links to the same target but changes the display to include the subscript. And again, it is allowed to pass just a substring or emtpy brackets to automatically add the subscript to the display:

#"Styles::list[MAX]"  --> list[MAX_STYLES] 
#"Styles::list[]"     --> list[MAX_STYLES]    

If a wrong subscript is given like this:

#"Styles::list[LIMIT]"  <- Wrong subscript

an error is reported:

Unresolved XLink #"Styles::list[LIMIT]".
  Collecting proposals of compound type(s) and base types: 
      Did you mean an entity in (C) class  dxl::Styles    tag:  /tmp/doxygenxlinks.tag:2012 ->:  file:///tmp/doxygenxlinks.html/classdxl_1_1Styles.html 
         (V) member-variable list[MAX_STYLES]             tag:  /tmp/doxygenxlinks.tag:2314 ->:  file:///tmp/doxygenxlinks.html/classdxl_1_1Styles.html#a9b5dd8f831faac30a9681f6c0f7cf56c 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:841:5

3.4 The XLink Display Text

Before we continue with the XLink-target syntax, this is a good time to introduce the display text syntax.

What we have learned so far in this manual is:

  • The Doxygen-command "\ref", by default uses the full qualified name of the entity to generate the link text. If this is not wanted, Doxygen offers to add an explicit display text to the link.
  • DoxygenXLinks is very different here:
    • The default display text is just the name of the entity – excluding its namespace/scope.
    • With functions: If all parameters are exactly given, then the default display text includes the parameters.
    • With variables: If some portion of a subscript is given, then the default display text includes the subscript.
    • With template types and template specializations: The default display text includes the template parameters as soon as just one parameter (or the keyword template) is given.

With this design, we assume that the DoxygenXLinks behavior allows omitting the provision of a display text in the great majority of cases, while the Doxygen behavior forces the user to add an explicit display text almost any time.

3.4.1 Overwriting The Display Text "

As formaly defined in the section 3.2 The XLink Syntax", the display text can be added to an XLink by appending a semicolon to the link target, followed by the text.
For example, the documentation sentence:

The module ALib Expressions uses #"VirtualMachine;a simple stack-machine" 
for the evaluation of expressions.

is translated to: "The module ALib Expressions uses a simple stack-machine for the evaluation of expressions."

Note
As you see, the font color of the link text is the same as the font color of the link target. However, the font face is the same as the surrounding documentation text. This is how the style-sheet of this documentation defines it. It can be adjusted as described in the chapter 2. CSS Link Styles for HTML Output.

3.4.2 Tweaking The Default Display Text

Besides just overwriting the default display text, DoxygenXLinks allows you to tweak the built-in display-text generation in a few ways. Tweaking is done by using "Display Tweaks" – special characters that are parsed from the start of the display-text. Parsing stops as soon as the first non-special character is encountered, or if the special character '/' is found.

The following table shows the special characters and their effect:

Character(s) Effect
'<' or '>' Adds template parameters and template specialization parameters in the case the target is a template type, respectively a template specialization (or both)
'(' or ')' Adds function parameters in the case the target is a function.
'[' or ']' Adds subscript in case the target is an array-variable.
'?' Adds the (return-) type in case the target is a function or variable.
'!' Adds the qualifiers in case the target is a function. With the same rationale given in section 3.3.4.2 By Qualifiers ", also the parameters of a function are added.
'*' Adds all of the above.
[1..9] Determines the number of parent scope levels to add to the display text.
'/' Disables the character recognition and displays the subsequent text as is.

Let's stick to the previous exampled entities and add some display tweaks:

1. #"Command(Jump);("    -> Command(integer, integer, JumpType)    
2. #"TString::IsEmpty;!" -> IsEmpty() const 
3. #"TString::IsEmpty;*" -> constexpr bool IsEmpty() const 
4. #"Styles::list;["     -> list[MAX_STYLES]     
5. #"Styles::list;?]"    -> const alib::String * list[MAX_STYLES]   
Note
The sample 3 above shows that the return type includes the word constexpr. While this is not effectively a return type, Doxygen's tag-file puts the keyword just there. We had considered parsing such keywords out of the return type but decided against it. Finally, this is some meaningful information for the user.
As always in such cases, your option is to not rely on the automatic display text generation of DoxygenXLinks, but provide your own display text instead.

Adding a Digit [1..9] To The Display:
When the display text tweaks contain a digit, then DoxygenXLinks will add the corresponding number of scope levels to the display text. A level of 1 is the default, hence passing 1 is equivalent to not adding a digit to the display tweaks. A level of 2 adds one parent scope level, and so on.,

Samples:

#"ArrayTraits;1"       No change                           --> ArrayTraits
#"Command(JumpType);2" Adds one parent scope level         --> Command::Command
#"Command(JumpType);6" Adds six parent scope level         --> alib::expressions::detail::VirtualMachine::Command::Command
#"XLink::scope;3"      Adds two parent scope levels        --> dxl::XLink::scope
#"XLink::scope;9"      Adds up to 9 parent scope levels,   --> dxl::XLink::scope
                       while in this case there are just    
                       two available.

The last link of the samples, results in a warning:

XLink #"XLink::scope;9" has a warning:
  Too many parents requested to be displayed
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:941:69

Warning summary: 1 XLinks with warnings.

3.5 Local Links

After this quick break into the display text syntax, we can move on introducing more features on selecting the XLink-targets. Next should be the topic of local links.

A local link is a link placed in the documentation of a code entity. Within this User's Manual, we cannot set local links and thus we can not easily sample them.

However, local links are very easy to use: Just add a leading '.' character to the link target, similar as if you were addressing a member of an object.
For example, the link

    #".Get" 

if placed in the documentation of class Placeholder, is translated to the link:

    #"alib::lang::Placeholder::Get"  ->  Get

Local links are short and easy to set when you document sibling members of a class. For example, when a constructor parameter is assigned to a field, then the parameter documentation might simply be

@param theField  Stored in the field #".myField".

Local links may also target inner types and their members. For example, if placed in the documentation of class Index, the link #".Node::Kind" targets the inner type Index::Node and within that, the member Node::Kind.

Technically this means that scopes are allowed to be given with local links. What is not allowed is to use #";scope hints" with local links (as it would not even make much sense). DoxygenXLinks will give you an appropriate error message in this case.

Attention
As helpful as local links are, there is a little catch about them: If you want to let Doxygen compile your documentation to other output formats (next to HTML), then local XLinks can become ambiguous. More details and how to mitigate this problem are provided in the chapter 4.1 Doxyfication: Restoring Sources to \ref Commands.

3.6 Linking To Preprocessor Macros

Note
On terminology: In this manual, we use the term "preprocessor constant" to refer to a preprocessor macro that has no parameter arguments.
For those that have arguments, we use the term "preprocessor macro".

DoxygenXLinks resolves links to preprocessor constants and macros. Here are three examples:

#"ALIB_VERSION"                --> ALIB_VERSION          
#"ALIB_ENUMS_MAKE_ITERABLE"    --> ALIB_ENUMS_MAKE_ITERABLE                      
#"ALIB_ENUMS_MAKE_ITERABLE;*"  --> ALIB_ENUMS_MAKE_ITERABLE(TEnum, StopElement)                        
#"ALIB_ENUMS_MAKE_ITERABLE;("  --> ALIB_ENUMS_MAKE_ITERABLE(TEnum, StopElement)                        
#"ALIB_ENUMS_MAKE_ITERABLE()"  --> ALIB_ENUMS_MAKE_ITERABLE(TEnum, StopElement)                        

As you see, preprocessor constants and macros are resolved correctly, and with macros, similar tweaks as for functions apply: The macro parameters are displayed, if

  • the '*' character is given, or
  • the '(' or ')' character is given, or
  • a subset of the parameters (or empty parameter brackets are given).
Note
Remember that with function targets, the parameters display is only triggered if all parameters are given. With macros, the parameter display is triggered as soon as just one parameter is given.
The rationale for this difference is that preprocessor macros cannot be overloaded. Hence, DoxygenXLinks assumes that the provision of the parameters or empty brackets was not intended by the documentation writer for disambiguation, but rather for displaying the full macro syntax.

Passing empty brackets directly to the name is preferred over the use of the display-tweaks '*', '(', or ')', because it this way DoxygenXLinks can perform a semantic check. These two links demonstrate the difference:

1. #"ALIB_VERSION;*"  -->  ALIB_VERSION                        
2. #"ALIB_VERSION()"                         

The second link results in an error:

Unresolved XLink #"ALIB_VERSION()".
  Did you mean (P) preprocessor constant ALIB_VERSION in file: A-Worx/ALib/src/alib/alib.inl    tag:  /tmp/alib.doxygen.tag:1805 ->:  file:///tmp/alib_html//alib_8inl.html#a46e5b5269a15bdcf84fb574fb130ee28 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1031:8

If wrong parameters are given like here:

#"ALIB_ENUMS_MAKE_ITERABLE(xyz)"  <- Wrong, not existing parameters

DoxygenXLinks also reports an error:

Unresolved XLink #"ALIB_ENUMS_MAKE_ITERABLE(xyz)".
  Did you mean (P) preprocessor macro ALIB_ENUMS_MAKE_ITERABLE(TEnum, StopElement) in file: A-Worx/ALib/src/alib/enumops/enumops.prepro.hpp    tag:  /tmp/alib.doxygen.tag:3908 ->:  file:///tmp/alib_html//enumops_8prepro_8hpp.html#a8df3f8533a890dcbbd2c0b9730bed2f7 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1041:5

You can also include the file name in the Link by adding a digit to the display tweak. For this, consider the following samples:

#"ALIB_VERSION;2"  --> ALIB_VERSION (alib.inl)                           
#"ALIB_VERSION;3"  --> ALIB_VERSION (alib/alib.inl)                           
#"ALIB_VERSION;4"  --> ALIB_VERSION (src/alib/alib.inl)                           
#"ALIB_VERSION;5"  --> ALIB_VERSION (ALib/src/alib/alib.inl)                           

As you can see, DoxygenXLinks adds the file name to the display text, and with increasing digits, the file name also includes its parent directories.
Consequently, the file path can also be used to disambiguate between different versions of the same preprocessor constant. We have added a test-case for this directly into the source code of DoxygenXLinks. The link:

#"TEST_CONSTANT"

produces the following output:

Ambiguous XLink #"TEST_CONSTANT".
  Could be (P) preprocessor constant TEST_CONSTANT in file: /home/dev/A-Worx/DoxygenXLinks/src/index.hpp    tag:  /tmp/doxygenxlinks.tag:85 ->:  file:///tmp/doxygenxlinks.html/index_8hpp.html#a594c887a0274a7745c610831bd96afe7 
  Could be (P) preprocessor constant TEST_CONSTANT in file: /home/dev/A-Worx/DoxygenXLinks/src/dxl.hpp      tag:  /tmp/doxygenxlinks.tag:25 ->:  file:///tmp/doxygenxlinks.html/dxl_8hpp.html#a594c887a0274a7745c610831bd96afe7 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1063:5

Disambiguation is done by adding portions of the file path as scope-hints. The following links resolve properly to the correct targets:

#"index TEST_CONSTANT;2" --> TEST_CONSTANT (index.hpp) 
#"dxl   TEST_CONSTANT;2" --> TEST_CONSTANT (dxl.hpp) 
Note
As of our knowledge, Doxygen's \ref-command does not support this kind of disambiguation.
Doxygen seems to just choose one of them, without notifying the user. The link \ref "TEST_CONSTANT" resolves to: TEST_CONSTANT.
Furthermore, we have not found a way to disambiguate between different versions of a preprocessor-definition in the same file. Doxygen here seems to be incomplete. We added the definition TEST_REDEFINED twice to this project's sources. While it nicely appears twice in the file's documentation (see here!), including the correct definition value, Doxygen adds the same documentation to both entries, thus ignoring the second definition's different text.

3.7 Linking To Doxygen Anchors

Doxygen allows to add anchors at any place in the documentation. For example, this section of the manual has an anchor defined by an extended Markdown syntax in the headline:

# 3.7 Linking To Doxygen Anchors # {#dxl_xl_anchors}

With that, the doxygen \ref-command and DoxygenXLinks can link to the anchor and the result is exactly the same:

3.7.1 Anchors Without Titles

Now let's use Doxygens's anchor-feature to link to the middle of a section. We add \anchor my_anchor right >here<.

Here are the corresponding links:

As you can see, the display-text is just the anchor name in both cases. This is because the anchor does not have a title. While Doxygen is silent about that, DoxygenXLinks shows a warning:

XLink #"my_anchor" has a warning:
  An anchor without a title was used without providing a user-defined display string.
  DoxygenXLinks (like Doxygen itself) inserts the anchor name in this case.
  To mitigate this warning, add ";1" to the link or add a reasonable display string.
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1102:5
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1111:22

To mitigate this, you can provide a display string for the anchor link. In the case that the anchor name itself has a nice name to be displayed already, you can simply use the ";1" syntax to continue to use the anchor name and just silence the warning:

#"ALIB_CMAKE_SKIP_THREAD_LIB_SEARCH;CMake variable"  -> CMake variable
#"ALIB_CMAKE_SKIP_THREAD_LIB_SEARCH;1"               -> ALIB_CMAKE_SKIP_THREAD_LIB_SEARCH
Note
This sample is an anchor to the description of a CMake-Variable of the ALib C++ Framework. As CMake is not supported by Doxygen, this anchor is set in the corresponding manual \page.

3.7.2 Ambiguous Anchors

Doxygen allows defining anchors of the same name at multiple places in the documentation. Nevertheless, it is not possible to disambiguate the link to such anchors.

For example, the anchor of the introduction section of this manual and that of the corresponding section of the ALib Programmer's Manual are both named intro.

The doxygen \ref-command just resolves to one of the occurrences of the anchor:

If an XLink is used instead:

#"intro"

an error is generated:

Ambiguous XLink #"intro".
  Could be docanchor  alib_manual::intro    tag:  /tmp/alib.doxygen.tag:50112 ->:  file:///tmp/alib_html//alib_manual.html 
  Could be docanchor  dxl_manual::intro     tag:  /tmp/doxygenxlinks.tag:3869 ->:  file:///tmp/doxygenxlinks.html/dxl_manual.html 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1148:5

From the error message, you can find the right disambiguation options:

#"alib_manual::intro" -> 1. Introduction
#"dxl_manual::intro"  -> 1. Introduction

Note that we chose the double-colon '::' for the domain-separation.

As usual, when spaces are used, the words become scope-hints: They can be abbreviated and are case-insensitive:

#"ALib intro" -> 1. Introduction
#"DXL intro"  -> 1. Introduction

In case that the anchors both reside on the root level (for example, if two pages with the same name originate from different manuals) then, the tag-file name may be used for disambiguation. For this let us create an anchor named "alib_manual""right here". With that, to be able to still link to the ALib manual, we can use the following links:

#"alib.doxygen.tag::alib_manual"  -> ALib Programmer's Manual  
#"alib alib_manual"               -> ALib Programmer's Manual           
Note
It is recommended to adhere to the Doxygen limitations and just name all anchors across all imported projects (tag-files) differently. However, in seldom cases, you might not have the possibility to change two imported conflicting anchors.
As just demonstrated, DoxygenXLinks solves this problem.
Sometimes Doxygen shows an error messge like this:
<alib_manual>:53602: warning: multiple use of section label 'alib_manual', (first occurrence: A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md, line 822)
To silence such warnings, you might pipe the doxygen output through a filter like this:
doxygen Doxyfile  2>&1 | grep -v "multiple use of section label"
But again, its better to use unique anchors if possible.

3.8 Linking To Files And Folders

DoxygenXLinks also supports linking to files and folders that are part of the documentation generated by Doxygen. Only files and folders that are actually indexed by Doxygen can be referenced. (I.e. files that appear in the generated documentation and include the \file-tag, folders that are documented using the \dir command, or those imported through tag-files.)

To create a link to a file or folder, the XLink kind specifier must be
set to 'F', respectively 'D':

#"F;dxlapp.hpp"      -->  dxlapp.hpp    
#"F;exception.inl"   -->  exception.inl 
#"D;strings"         -->  strings    

The reason why the kind specifier is mandatory with files and folders, and that those are not just used for disambiguation, is of a technical nature: With those, DoxygenXLinks switches into the "file-linking mode". In this mode:

  • The dot (.) is treated as a literal character and not as a namespace or compound separator.
  • The remainder of the target is interpreted as a file name or file path.
  • Resolution is restricted to files and folders only; code entities and other target types are not considered.

Using a display tweak, we can add portions of the file path to the link:

#"F;exception.inl;1"   -->  exception.inl 
#"F;exception.inl;2"   -->  exceptions/exception.inl 
#"F;exception.inl;3"   -->  alib/exceptions/exception.inl 
#"F;exception.inl;4"   -->  src/alib/exceptions/exception.inl 
#"F;exception.inl;5"   -->  ALib/src/alib/exceptions/exception.inl 
#"F;exception.inl;6"   -->  A-Worx/ALib/src/alib/exceptions/exception.inl 
#"F;exception.inl;7"   -->  A-Worx/ALib/src/alib/exceptions/exception.inl  <- no more additonal parents 

As with links to code entities, file links may be disambiguated using scope-hints - here better named "path-hints". Path-hints are specified as space-separated tokens before the file name and are matched as case-insensitive, ordered substrings against the file’s relative path. In this example:

#"F;alib src exception.inl"  --> exception.inl

the hints alib and src are used to narrow the search to files whose path contains these words in this order.

If necessary, parts of the file’s relative path may be included directly separated by slashes:

#"F;exceptions/exception.inl"  --> exception.inl
#"F;exceptions\\exception.inl" --> exception.inl  (boths slash-types are accepted)

Of course, a custom display text may be specified using the standard XLink syntax:

#"F;exception.inl;ALib's exception implementation" -> ALib's exception implementation

3.8.1 Linking Directly Into Source-Pages

When linking to source files, DoxygenXLinks provides some little extra candy. With the Doxygen-syntax a file-link always targets a dedicated generated description page first. You can see a sample by clicking the following link:

Within this page you'll see a link titled "Go to the source code of this file". DoxygenXLinks allows you to target the source code directly by prepending a circumflex character '^' to the link:

#"F;dxlapp.hpp;Link to the file's contents page"  ->  Link to the file's contents page 
#"^F;dxlapp.hpp;Link to the file's source code"   ->  Link to the file's source code   

The cirumflex character '^' is used in other, similar contexts by DoxygenXLinks. Consider this character as somthing like an arrow, indicating some traversal. More on this is given already in the next chapter.

3.9 Indirect Linking: Inherited Members And Those Of Underlying Types

This subsection of the XLink-syntax discussion turns back to the topic of linking to code entities.

Doxygen's \ref-command transparently supports linking to inherited members. This is likewise supported by DoxygenXLinks:

#"DXLApp::bsConfigureCLI;2" -> DXLApp::bsConfigureCLI  // links into ALib's class AppCLi, which is the parent of DXLApp
#"FTree::checkChildName;2"  -> FTree::checkChildName   // links into class StringTreeBase, which is a grandparent fo class FTree

Furthermore, linking to members "through" type-definitions is possible:

#"alib::strings::TString;3" -> alib::strings::TString    // This is a templated string-type, similar to <c>std::basic_string_view</c>"
#"alib::String;2"           -> alib::String              // This is a non-templated type definition, similar to <c>std::string_view</c>"
#"alib::String::Length;3"   -> strings::TString::Length  // This is a link into the underlying class \b TString, using the type definition as the scope!"

Finally, both indirect linkage types can be mixed:

#"ChainedAString"            -> ChainedAString          // A chained type definition
#"ChainedAString::Length;2"  -> ChainedAString::Length  // Resolves the chained type definition, then the base class
#"TestAString::Length;2"     -> TestAString::Length     // Resolves the base class, then the chained type definition

However, while Doxygen is very silent on this feature, DoxygenXLinks shows warnings when you use indirect linkage. For example, the XLink

#"AString::Length;2" -> AString::Length

generates the message:

Warning: XLink #"AString::Length;2" has an indirect target but is not marked with the indirection prefix '^'.
  Target: (M) member-function alib::strings::TString::Length() const    tag:  /tmp/alib.doxygen.tag:40971 ->:  file:///tmp/alib_html//classalib_1_1strings_1_1TString.html#a29c116f2868763737b2012466d78729d 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1295:5

The rationale for doing so is again, that a writer of the documentation should be aware of what he is doing and be explicit about it in the documentation source. For example, when writing

The length of the string can be retrieved with the method #"AString::Length;2".

the word "inherited" should be inserted:

The length of the string can be retrieved with the inherited method #"AString::Length;2".

As the warning states, to mitigate it, the circumflex character '^' can be added to the start of the link.

#"^alib::strings::TString;3" -> alib::strings::TString  // no warning
#"^alib::String;2"           -> alib::String            // no warning
#"^alib::String::Length;3"   -> strings::TString::Length   // no warning
Note
We chose '^' because it looks a little like an up-arrow, indicating inheritance. While this includes the mitigation of lookup-warnings for underlying types, you should remember it as an indirection prefix.

DoxygenXLinks also warns on the opposite case: Imagine you wrote about using an inherited method and you duly added the indirection prefix to its link. Now, you extend your software and the class you are referring to, now overrides the methods with an own version. DoxygenXLinks will then warn you that your previously valid link is now invalid.

As a sample, the XLink

#"^TAString::operator[];2" -> TAString::operator[]     

generates the following warning:

Warning: XLink #"^TAString::operator[];2" uses indirection prefix '^', while it targets a direct member in the parent
scope.
  Hint: Remove the prefix from the XLink
  Target: (M) member-function alib::strings::TAString::operator[](integer)    tag:  /tmp/alib.doxygen.tag:34267 ->:  file:///tmp/alib_html//classalib_1_1strings_1_1TAString.html#a221d85f204caba63befa9e463ceebb15 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1331:5

With indirect linking, the automatic display string generation uses the scope-path given with the link string and not the target's scope! Hence, if you want to use, for example, ;3 to show the last three path components, you have to provide a minium of two parent scopes with the link. (Otherwise, DoxygenXLinks will give you a warning.)

Note
In general DoxygenXLinks allows to use indirection in combination with local links. This means, if you have an inner type that inherits from a base class, within the outer type, you can use the dot '.'charcter to address the inner type and then add furher member-scopes that are inherited from the base class. This might look like this:
  #"^.InnerType::inheritedMember" 
While this often works, sometimes Doxygen misses the base-type information of inner types in the generated tag-files. We have not found a rule that explains when this happens and when not. Thus, before testing around too long, just drop the local link and the indirection syntax, and use a direct link to the inherited type instead. If you really need the indirection to be shown in your documentation, override the display string of the link. In the sample, this then could look like this:
  #"BaseType::inheritedMember;InnerType::inheritedMember"

3.10 Targeting Underlying Types of Type-Definitions

As described in the previous section, the prefix character '^' is used as an "indirection prefix" in the case that:

  • A parent scope is specified with the target identifier, and
  • this parent scope is either a record (class, struct, or union) or a type-definition (typedef or using).

Now, DoxygenXLinks reuses this character '^' to resolve to the "underlying type" of a type-definition. This means if:

  • the character is given as a prefix (as if it was an indirection prefix), while
  • the target's parent (no matter if provided with the link or not) is a namespace, and
  • the target itself is a type-definition,

then the link is resolved to the underlying type of the type-definition!

Here is an example:

#"alib::String;2"   -> alib::String   // resolves to the type-definition alib::String
#"^alib::String;2"  -> alib::String   // resolves to its target alib::strings::TString

Likewise with other indirect links, when linking to underlying types, the automatic display string generation uses the scope-path given with the link string and not the target's scope! Hence, if you want to use, for example, ;3 to show the last three path components, you have to provide a minium of two parent scopes with the link to the type definition. (Otherwise, DoxygenXLinks will give you a warning.)

When the prefix '^' is used like this, then a disambiguation rule is applied to the XLink: Type-definitions are preferred over other entities. For example, the following link is ambiguous:

#"String"  // ambiguous link

and produces:

Ambiguous XLink #"String".
  Could be (T) typedef alib::String                                       tag: /tmp/alib.doxygen.tag:45140 ->:  file:///tmp/alib_html//namespacealib.html#a741b6610debe8ddae80beb3dbd74c53d 
  Could be (A) enumvalue alib::format::FormatterStdImpl::PHTypes::String  tag: /tmp/alib.doxygen.tag:19947 ->:  file:///tmp/alib_html//classalib_1_1format_1_1FormatterStdImpl.html#a5ecaa0736152b685865b846b2da88e23a27118326006d3829667a400ad23d5d98 
  Could be (V) member-variable alib::expressions::Types::String           tag: /tmp/alib.doxygen.tag:42077 ->:  file:///tmp/alib_html//structalib_1_1expressions_1_1Types.html#ac98596ea00e1d87dcd7003f8d00aeac3 
  @ /home/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1381:5

If the link is prefixed with '^':

#"^String" -> String

the type definition is chosen and resolved to the underlying type!

Note
Targeting the underlying type of type-definitions, should be used with a little caution. The reader of the documentation expects a link to a type called "String", but ends up with a link to a type called "TString".
For users of the ALib C++ Framework this is not a problem, because almost any type of that library mirrors an alias name in the outer namespace alib. An ALib user is used to work with such alias-types. They are widely used in the documenation, and this fact is explicitly documented here!

3.11 Prefix '%' To Suppress Linking

The following table is a copy of the table of the manual chapter 2. CSS Link Styles for HTML Output, but this time, all XLinks are prefixed with '%'.
For example, instead of the link:

#"VirtualMachine;2"

this table contains:

#"%VirtualMachine;2"

Here we go:

Link Target Type Sample Internal Sample External
Namespace dxl expressions
Namespace Function ConvertHTMLEntitiesToAscii ScanFiles
Namespace Variable FindInheritedMembers GLOBAL_ALLOCATOR
Union dxl::DXLTestUnion boxing::Placeholder
Struct AnchorKey detail::VirtualMachine
Class dxl::XLink expressions::Compiler
Member Function XLink::Parse TString::IndexOf
Member Variable XLink::IsLocal TString::length
Enumeration Kinds Verbosity
Enum. Element Target::EnumElement Verbosity::Info
Type definition ChainedAString alib::String
Resolved definition ChainedAString alib::String
Preprocessor macro TEST_CONSTANT ALIB_ENUMS_ASSIGN_RECORD
Source folder src alib/enumops
Source file styles.hpp alib/enumops/bitwise.inl
Page/Group/Anchor 2.2 CSS Styles Added By DoxygenXLinks 2.3 Tabular List Of Modules

As you see, prepending the percent symbol '%' to the very start of an XLink lets DoxygenXLinks not setting an anchor. Instead it inserts either a <span> or a <code> HTML-tag around the link's display text.
The inserted tag is equipped with the very same CSS-classes as the link's anchor - it is only missing the initial class "el" which Doxygen adds to all anchors.
Now, our custom style sheet is designed to hide the colors in this case, but what is still visible is the the changed font-style in accordance with the XLinks target type.

You may wonder what this is good for! The answer is simple and becomes obvious once you start writing longer documentation texts. For example, when a certain class is described, its first mention should be a link to its reference page. But the repeated mentioning of the class in the text should most probably not be a link. If it was, the reader would have to keep in mind, which types he already had seen in detail before. Whenever a reader stumbles accross a link he tends to wonder if this is a new type or function. In addition, the documentation text would become too colorful and in general too messed up with anchors.
On the other side, it is helpful if any repeated mentioning of a type in the documentation still uses a different font style than the rest of the text.

If a documentation is consequently written in the way that all mentioning of types, functions, variables, etc., are always either normal XLinks or ones suppressed with '%', then a further huge advantage arises: In the case of refactoring the code, DoxygenXLinks will either:

  • fix the display text automatically, or
  • give an error about an invalid entity.

In other words: Using suppressing links, adds semantic value to the documentation sources and makes it easier and less error-prone to maintain.

4. Additional Features

This manual is already quite long, and we have covered the two main features of DoxygenXLinks, namely

In the following sections, some further features are discussed, the first one of which was a mandatory to do for us to close the documentation build process loop!

4.1 Doxyfication: Restoring Sources to \ref Commands

DoxygenXLinks provides an option to replace all your XLinks back to original Doxygen links, aka \ref-commands. This is needed when you want to have Doxygen generating not only HTML-output, but make use of the various output options that Doxygen provides.

Note
If you want to fully understand why this is necessary, please read the manual chapter 7.3 Implementation Overview.

In this case, your workflow is as follows:

  1. Create a copy of your source folders. For this, you might want to place a copy command in your documentation build-workflow. Such copy may reside in a temporary folder.
    Your script at its very start should:
    • clean the temporary source-copy folder
    • copy the source files and folders into the temporary folder.
  2. Run Doxygen using a configuration file which generates exclusively HTML output.
  3. Run DoxygenXLinks with the option:
    --doxyfy=/path/to/your/temporary/source_copy
    
  4. DoxygenXLinks will process as usual, and as a final step it replaces the XLinks in the all of the files found in your copy folder (and its subfolders) with normal \ref commands.
  5. Run Doxygen again, this time with a different configuration file, which
    • uses the temporary copy folder as its input, and
    • in this run generating the other output formats.
Note
Of course, you can use this feature also to restore your Doxygen links to their original form if you want to dispose DoxygenXLinks from your documentation build-workflow. If so, please contact us and tell us what you do not like about it!
Attention
There is one minor caveat to this approach: When you use local links in your documentation, the following problem can occur:
If you have the same local link used twice in different entities, let's say #".Do", then DoxygenXLinks cannot distinguish between the two links when it finds them in the source files. The technical reason for this is: When DoxygenXLinks processes the HTML-files (its standard procedure), the name of the HTML file is used to determine the right scope of the given entity. As long as you use the same link in only one entity, all is fine, because DoxygenXLinks sees this link located in only one HTML-file. If two or more entities use the same link, then DoxygenXLinks cannot distinguish between them at the time the –doxyfy option is processed.
This is a technical limitation arising from the fact that DoxygenXLinks is just a post-processor for Doxygen.

What was explained above in the red box can quite easily be mitigated. If you constantly use the –doxyfy option every time you compile your documentation, DoxygenXLinks will warn you about it such cases like this:

XLink #".Do" is erroneous:
  This error is set, with the command line option '--doxyfy'.
  As explained in the user manual, equal local links cannot be restored back to
  Doxygen's \ref command if they occur in different HTML files. Please check each
  location below and manually correct the link to target the right local entitiy.
  @ ./DoxygenXLinks/srccopy/jobs.hpp:100:53
  @ ./DoxygenXLinks/srccopy/jobs.hpp:112:47
  This local XLink's HTML file: structdxl_1_1SourceLocationFinder.html

The final line of this message tells you which HTML file was used to determine the scope of the \ref-command. This scope is used with both replacements, so one of them is falsely determined. The other one is correct.

As soon as you see such a warning, the fix is straightforward: Turn one of the XLinks into a non-local version. It does not matter which one!

4.2 Runtime Expression Language

DoxygenXLinks comes with a runtime expression language that allows you to query the XLinks that you have set in your documentation. With that, you can answer questions like:

  • "Which <b>%XLinks</b> are linking to a namespace?"
  • "Which <b>%XLinks</b> are linking to a class?"
  • "Which <b>%XLinks</b> are linking a function?"
  • "Which <b>%XLinks</b> are linking indirectly through type definitions to a type?"
  • "Which <b>%XLinks</b> are have a target whose name contains the string "Get" and no display text set?"

Overall, the language consists of more than 50 dedicated DoxygenXLinks analysis functions, more than 150 operators and further functions for string manipulation, etc.

You may wonder: What is this now good for? Well, honestly, it was a low hanging fruit for us: Being built on the ALib C++ Framework, its module ALib Expressions was on hand, and this makes it fairly easy to integrate customized runtime expressions.

So, lets start!

4.2.1 Command-Line Option –LIST

The command-line option –LIST allows you to pass a query string to DoxygenXLinks, which is processed at the end of its run, after all the documentation is built.

Note
For each new query you have to compile your documentation again. But taking the performance numbers of the previous section into account, this is not a big deal.

Here is an example which is run when generating the docuumentation of this tool itself (the one you are reading right now):

--LIST='Name=="Index"'

The output is:

#"dxl Index" -> \ref dxl::Index "Index"  
  @ /mnt/a/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:981:24
  @ /home/dev/A-Worx/DoxygenXLinks/src/target.hpp:21:16

#"Index" -> \ref dxl::Index "Index"  
  @ /mnt/a/dev/A-Worx/DoxygenXLinks/src/index.hpp:171:25

#"dxl::Index" -> \ref dxl::Index "Index"  
  @ /mnt/a/dev/A-Worx/DoxygenXLinks/src/target.hpp:244:73

List summery: Found 3 XLinks @ 4 locations that match expression Name == "Index".

As you see, the class Index is linked 4 times in the documentation sources using 3 different XLinks.

This query asks for all links that directly target a source file (as described at in the sub-section 3.8.1 Linking Directly Into Source-Pages:

--LIST='IsIndirectSourceFile'

The output is:

#"^F;dxlapp.hpp;Link to the file's source code" -> \ref dxlapp.hpp_source "Link to the file's source code"  
  @ /mnt/a/dev/A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md:1261:60

So this was done only once in the documentation sources.

...todo: more samples to add here.

4.2.2 Command-Line Option –FORMAT

The sample-queries of the previous section all showed a certain output format, which somehow shows how an XLink found is translated to a \ref-command.

This is just the - rather nonesense - default output format. You can change what DoxygenXLinks prints per query with the command-line option –FORMAT. If not given, its default value is:

--FORMAT='\#{LinkString@!Q} -> \\ref {path} {Display@!Q}'

...todo: more information where to find the available format-specifiers.

4.2.3 Expression Reference

The complete list of available expressions functions that are dedicated to XLink queries is described with the reference documentation of class DXLExpression.

Further information on the other expressions functions is found with the ALib documentation: A.1 Built-In Identifier, Function And Operator Reference.

4.3 Statistics

Should you be interested in some statistics about your documentation, you can pass the option

    --STATISTICS=On 

to DoxygenXLinks. For example, when building the documentation of the ALib C++ Framework , the following output is produced:

Statistics of command: DoxygenXLinks /tmp/alib_doxyfile.ini --STATISTICS=on
====================================================================================================

Tag-files:
============
  No 1: 51,694 lines read, file "/tmp/alib.doxygen.tag", URL: file:///tmp/alib_html, load time 010 ms

   Type           |alib.doxygen.tag 
  ----------------|-----------------
   dir            |              34 
   file           |             346 
   page           |              44 
   group          |               0 
   docanchor      |             924 
   namespace      |              62 
   struct         |             361 
   class          |             172 
   union          |              10 
   concept        |              30 
   define         |             348 
   typedef        |             671 
   variable       |           1,430 
   function       |           3,417 
   enumeration    |              91 
   enumvalue      |             618 
   generic_member |               0 
   UNKNOWN        |               0 
  ----------------|-----------------
             Sum  |           8,558 

XLinks:
============
                 |  Sources |    HTML  | Unique/Total 
  ---------------|----------|----------|--------------
  XLinks         |   11,240 |   11,509 |    4,205
      Unresolved |        0 |        0 |        0
       Ambiguous |        0 |        0 |        0
       Erroneous |        0 |        0 |        0
        Warnings |        0 |        0 |        0
  EL-Anchors     |      -/- |   77,596 |   77,596
      Unresolved |      -/- |        0 |        0
  ELREF-Anchors  |      -/- |        0 |        0
      Unresolved |      -/- |        0 |        0
  ---------------|----------|----------|--------------
           Files |      511 |    1,697 |    2,208
           Lines |  154,430 |  486,690 |  641,120
            Size |   7.2MiB |  32.7MiB |  39.9MiB
            Time |   032 ms |   023 ms |   056 ms

Total Time: 064 ms     

The statistics should be quite self-explanatory. Only the lines "EL-Anchors" and "ELREF-Anchors" might need explanations: These are the anchors that DoxygenXLinks finds in the HTML-files, which either result from remaining Doxygen \ref-commands (not applicable for that project as all links have been replaced by XLinks), or are automatically generated by Doxygen. For example, when a function of a documentated class returns an object of a likewise documented type, then Doxygen generates an anchor for naming that return type.

So as you can see:

  • ALib has 8,558 documented code entities.
  • DoxygenXLinks found 11,240 XLinks in the HTML-files, which is a little bit more than those found in the source files. This is because Doxygen copies some documenation pieces to index pages, etc.
  • Of these 11,240 XLinks, 4,205 are unique. In other words, each link occurs around three times in the sources. (DoxygenXLinks separates two XLinks that share the same target but have other options, like a different display set.)
  • The tag-file that Doxygen generates consists of 51,694 lines!
  • The number of replacements in HTML files is 77,596 + 11,509 = 88,105.
  • The total time spent by DoxygenXLinks is 64 ms.
  • We said "Wow!" when we first saw that!

4.4 Logging

Todo:

  • Add an option for verbosity
  • Dox here...

5. Usage (Calling The Tool)

DoxygenXLinks is post-processor to Doxygen. Because it takes all necessary information from the Doxyfile (that you anyhow should create), calling the tool is very simple:

  1. Run Doxygen First, generate your documentation with Doxygen as usual.
  2. Run DoxygenXLinks passing the path to the same Doxyfile you used for Doxygen:
    doxygen  /path/to/Doxyfile
    doxygenxlinks /path/to/Doxyfile
    

OK, there is also one prerequisite: You have to tell Doxygen to generate a Tag-File. This is done by setting the option GENERATE_TAGFILE in your Doxyfile .

GENERATE_TAGFILE = myproject.tag

This tells Doxygen to produce an XML tag-file that DoxygenXLinks will parse and create its index from.

If you are importing further documentation into your project (see Doxygen manual here ), then also DoxygenXLinks will use these tag-files to resolve links. With that, the use of DoxygenXLinks is transparent for you.

Note
The manual that you are just reading is likewise generated with Doxygen and the DoxygenXLinks post-processor. Because DoxygenXLinks is built using ALib, we used its types and functions in the samples of the previous sections. For this, the Doxyfile of DoxygenXLinks simply imports the Tag-File of ALib.

5.1 The –help Option

The DoxygenXLinks tool comes with a help option that lists all available command-line options. Its output is quite verbose and we just repeat it here to avoid redundant work for us.

\include docs/dxl_call.txt

While most of this is self-explanatory, here are some further hints:

5.1.1 Option –verbosity

5.1.2 Option –configfile

DoxygenXLinks supports a configuration file that can be used to set default options for the tool. If the option –configfile is not given, DoxygenXLinks will not create one silently. Instead, it will read all information from the command-line and/or the environment variables.

If the option –configfile is given but the file does not exist, then DoxygenXLinks will create a configuration file with the given name. That file will be populated with all options and their default values, respectively those values that were set on the command-line.

If it is absolute, it is interpreted as an absolute path. If the given file path is relative, it is interpreted as relative to the current working directory. If it is neither of these (just a file name), then DoxygenXLinks will place it in the configuration directory of the user (which is system dependent).

The file is called dxl.config and is located in the same directory as the DoxygenXLinks tool.

6. Building DoxygenXLinks

todox

7. Further Thoughts & Details

7.1 Less Error-Prone Documentation

Using DoxygenXLinks for linkinging within your documentation supports you to write documentation that is more error-prone than using the \ref-command or the #-auto-linking provided by Doxygen.

To achieve this, it is advised to add some extra information to your link targets, that would not be needed to disambiguate the link. For example, rather than using display tweaks to, for example, add a functions' parameter list to the link text, add the parameter list to the link itself.
With this, the link will always resolve to the same target, even if an overload of that function is added. And also, if the parameter list changes, then DoxygenXLinks will report an error and you are asked t o fix the link in your documentation.
When doing this, you have the chance to review your documentation and make sure that changes are reflected.

Also, you should discipline yourself and use suppressed links feature whenever your write the name of a class, a function or any other code entity. This way, refactorings of the code base will result in the complete list of necessary name- or scope changes of your documentation the next time you run DoxygenXLinks. And even without that, it just results in a nicer looking documentation.

7.3 Implementation Overview

DoxygenXLinks is built using the ALib C++ Framework . Especially, ALib's powerful memory management, string processing and multi-threading features are leveraged to ensure fast processing even for very large documentation sets. Furthermore, ALib's CLI handling capabilities and ALib's Application Framework are used to provide a command-line line processing, configuration and logging facilities, time measurement, exception- and error handling, and some more. Finally, ALib provides runtime-expression evaluation engine that is used to implement the query feature.

The steps that the tool takes are:

  1. Parses your Doxyfile to find
    • the input files and folders,
    • the HTML output directory, and
    • the tag-files.
  2. Loads all tag-files into a alib::containers::StringTree and additional alib::containers::HashTables for lightning-fast lookups.
  3. Scans your generated HTML files for XLinks.
  4. Resolves those links and updates the HTML files with proper anchors, equipped with extended CSS classes.
  5. In the same run over the HTML files, all automatic anchors set by Doxygen are equipped with CSS classes. For this, the tool performs reverse-lookups of the anchor's targets as given by the tag-files.
  6. Scans your source files to identify all original source-locations the XLinks that were found in the the HTML files.
  1. Generates well-sorted output on unresolved or ambiguous links, including proposals for correcting the links.
  2. Generates output in the case that XLinks were found in the source files, but not in the HTML files. (This especially happens if DoxygenXLinks was run before Doxygen was run, or was run twice, or on other configuration errors.)
  3. If parameter –DOXYFY is given, scans all source files in the copy-folder and replaces XLinks with standard \ref-commands.
  1. If parameter –STATISTICS is given, generates statistics about the run.
  2. If parameter –LIST is given, performs a query and prints the results.







*/