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:
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.
Before we dive into the details of DoxygenXLinks, let's take a look at the features it offers:
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.
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:
Doxygen itself exposes only two separate CSS-classes for links:
"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."elRef" for links into external documentation.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.
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 |
"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. 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.
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:
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:
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.
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.
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:
'%'-markers to suppress the auto-link. This and other considerations (i.e that the link display text is not changeable) renders the Autolink-feature unsuitable for larger projects.
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.
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).
To resolve the ambiguity, in that case we have two options:
"::" to narrow down the search.#"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:
#"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)
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.
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).
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.
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 |
DoxygenXLinks has two options to disambiguate overloaded functions.
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:
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.
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.
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:
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:
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)
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:
template,"< >", 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.
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>
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
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:
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.
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."
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]
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.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.
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.
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
'*' character is given, or'(' or ')' character is given, orPassing 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)
\ref-command does not support this kind of disambiguation.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:
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
\page. 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
<alib_manual>:53602: warning: multiple use of section label 'alib_manual', (first occurrence: A-Worx/DoxygenXLinks/docs/dxl_manual.dox.md, line 822)
doxygen Doxyfile 2>&1 | grep -v "multiple use of section label"
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:
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
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.
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 word "inherited" should be inserted:
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
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.)
'.'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"
#"BaseType::inheritedMember;InnerType::inheritedMember"
As described in the previous section, the prefix character '^' is used as an "indirection prefix" in the case that:
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:
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!
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:
In other words: Using suppressing links, adds semantic value to the documentation sources and makes it easier and less error-prone to maintain.
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!
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.
In this case, your workflow is as follows:
--doxyfy=/path/to/your/temporary/source_copy
\ref commands.#".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.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!
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:
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!
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.
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.
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.
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.
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:
Todo:
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:
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.
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:
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.
todox
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.
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:
\ref-commands.
*/