Documentation Buy Contact Blog

DocuDuck - C++ API Reference Building and Validating

16 September 2013

Since Number Duck is a programming library built to be used by others, I'm putting a lot of work into having good documentation. So, along with fun examples, I have developed DocuDuck, a PHP script that will generate the HTML API reference for as part of the automated build process. Also, DocuDuck checks the documented functions against the actual C++ code, and will error if anything is out of date or missing, thus failing the build.

You can download DocuDuck here. It includes all the dependencies to run on windows, but you'll need to have PHP and SWIG installed to run it on Linux. Below is a description of how it works and how to set it up for use with your own projects.

Here's the class we will be documenting in this example:

#ifndef CRAB_H
#define CRAB_H
	class Crab
			void Scuttle();
			float CalculateCircleArea(float fRadius) const;

For the documentation itself, I have that stored in an xml file for easy editing/parsing. Heres an example:

<?xml version='1.0' standalone='yes'?>
<library name='Example'>
	<class name='Crab'>
			Crabs can scuttle and calculate the area of a circle.

		<function name='Crab' returntype=''>
				Unassuming constructor.

		<function name='Scuttle' returntype='void'>
				Do a silly sideways crab walk.
		<function name='CalculateCircleArea' returntype='float'>
			<parameter name='fRadius' type='float'/>
				Calculates the area of a circle from the _fRadius_ given.

As you can see it's pretty straightforward, with each class and member function laid out with parameters and descriptions. Also note that Markdown is being used in the description fields.

This xml file is processed by a php script that converts this xml into nice html using Twig, a simple web templating engine.

{% macro function(pFunction) %}
	<span class='keyword'>{{ pFunction.m_sReturnType }}</span> <span class='identifier'>{{ pFunction.m_sName }}({% for pParameter in pFunction.m_pParameterArray %}{{ pParameter.m_sType }} {{ pParameter.m_sName }}{% if loop.last != true %}, {% endif %}{% endfor %})</span>
	{% if pFunction.m_bConst == true %}
		<span class='keyword'>const</span>
	{% endif %}
{% endmacro %}
{% import _self as robot %}

{% for pClass in pClassArray %}
	<h2><a id='{{ pClass.m_sName }}'>{{ pClass.m_sName }}</a></h2>
	<div>{{ pClass.m_sDescription|raw }}</div>
	{% for pFunction in pClass.m_pFunctionArray %}
		<h3><a id='{{ pClass.m_sName }}_{{ pFunction.m_sName }}'>{{ robot.function(pFunction) }}</a></h3>
		<div class='functionBody'>
			{{ pFunction.m_sDescription|raw }}
	{% endfor %}
{% endfor %}

This is a cut down section of the full Template.html. The full file with all the HTML boilerplate and pretty CSS is available in the download. The first block is a reusable macro to output a full function definition with parameters and highlighting for keywords and types. The second block loops through all the classes and outputs the formatted HTML documentation.

This is a nice start, but when we change the code, there's nothing to enforce updating the documentation. So what we need to do is compare the XML documentation from above with the actual C++ file to make sure they are in sync.

All the hard work is done by SWIG, the Simplified Wrapper and Interface Generator. As the name implies, SWIG can be used to generate wrapper code for your classes, such as generating a code that would allow your C++ code to be used in Java. In our case we use the XML output which is a simple dump of SWIGS internal representation of the parsed C++. SWIG seems like an overpowered tool for what we are looking to do, but it's easy to get running and the XML output is far easier than trying to parse the C++ code ourselves.

%module Example

#include "Crab.h"

%include "Crab.h"

Note that the SWIG config file is a little weird. SWIG does not actually follow the #include directives in your code, so you have to manually list any includes in the top section. Then list the files to parse in the bottom section. You get a kind of cryptic error if you forget to add the includes to the top section, so I just add them to both to avoid any confusion :P.

DocuDuck will then dig through the SWIG output and compare it to the Example.xml documentation. If everything checks out, it will then generate the final HTML output.