rdoxygen: Documentation of C++ source code in R

Motivation

A fundamental requirement of contributed R packages is that they contain complete, standardized documentation of all exported functions. Not only is this a courtesy to package users, but also of great value to package developers to effectively organize, update, and collaborate on their code. While the bare-bones method of creating R documentation is somewhat cumbersome, the task is immensely simplified by the processing of comment markup enabled by roxygen2.

Unfortunately, documentation is not required of package C++ code – to the detriment, in our opinion, of clarity and efficiency of such code in many R packages. Thus, the purpose of rdoxygen is to provide a few simple tools for R package developers to generate documentation for their C++ code. At the C++ level, rdoxygen uses doxygen, a powerful and flexible C++ code parser to convert roxygen-style comments into HTML documentation. At the R level, rdoxygen provides a simple interface to the doxygen engine, and the option of accessing its generated documentation through the mechanism of R vignettes.

Walkthrough

Installation

To use rdoxygen you need to first install doxygen, for which detailed instructions are provided here. Next, install rdoxygen either from CRAN, or obtain the latest development version from Github with devtools::install_github().

Doxygen Markup

The following is a C++ code snippet taken from Rcpp Modules, with added doxygen-style comments to be parsed into source code documentation. For simplicity only a few doxygen features are illustrated here; the complete set is extensively documented on the doxygen website.

/// A class for uniform random number generation.
///
/// Provides an example of doxygen class documentation.
class Uniform {
public:
  /// Construct a uniform random number generator.
  Uniform(double min_, double max_) : min(min_), max(max_) {}

  /// Obtain iid draws from a uniform distribution.
  NumericVector draw(int n);

  double min; ///< Minimum value of the uniform.
  double max; ///< Maximum value of the uniform.
};

/// Creates an object to sample from \f$U \sim \mathrm{Uniform}(a, b)\f$.
///
/// @param[in] min_ The minimum value \f$a\f$ of the uniform.
/// @param[in] max_ The maximum value \f$b\f$ of the uniform.
Uniform::Uniform(double min_, double max_) : min(min_), max(max_) {}

/// Returns a sample \f$U_1,\ldots,U_n \stackrel{\mathrm{iid}}{\sim} \mathrm{Uniform}(a, b)\f$.
///
/// @param[in] n Number of iid draws to produce.
/// @return Vector of `n` draws from the uniform distribution.
NumericVector Uniform::draw(int n) {
  RNGScope scope;
  return runif( n, min, max );
}

Processing with rdoxygen

Suppose that the code snippet above is in a file called rdoxygenExample.cpp. Then a typically doxygen processing work flow is as follows:

  1. Create a default Doxyfile containing a list of options to render the documentation in rdoxygenExample.cpp.
  2. Edit the Doxyfile to customize rendering options as desired. The relevant settings are extensively documented on the doxygen website and within the default Doxyfile itself.
  3. Run doxygen on the Doxyfile to create the documentation HTML.

The rdoxygen package provides several convenience files to do all of this from within an R session during the package development process. That is, suppose rdoxygenExample.cpp is located in the src folder of the R package DoxygenExample. From an R session with working directory anywhere within the folder structure of DoxygenExample, the package developer can parse the doxygen documentation (doxydoc) with the following R code:

require(rdoxygen)

# create doxydoc with default options, wrap it as an R vignette
doxy(vignette = TRUE)

# --- separate the steps above ---

# 1. Create just the Doxyfile for the package documentation.
#    In particular, this looks for any doxygen markup in
#    the src and inst/include subdirectories.
doxy_init()

# 2. Optionally, edit the package Doxyfile
doxy_edit(options = c(SHOW_INCLUDE_FILES = "NO"))

# 3. Create the doxygen HTML documentation
doxy(vignette = FALSE)

# 4. Wrap the HTML documentation into an R vignette
doxy_vignette()

The HTML output of these calls can be viewed here.

The package also provides an RStudio Addin named rdoxygenize that binds to the first command doxy(vignette = TRUE), which can then be called with a keyboard shortcut (e.g. CTRL+SHIFT+R). This makes the process of creating the doxydoc comparable to that of creating roxygen2 documentation via the usual RStudio command sequence CTRL+SHIFT+D.

Compatibility with devtools

As documented here, the call to vignette() will only open HTML files stored in the doc subfolder of an installed package. Therefore, a natural location for the doxydoc is in inst/doc/doxygen. However, the latest version of devtools incontrovertibly deletes inst/doc during the build/install process (as documented here). Due to the ubiquitous usage of devtools among R package developers, the doxydoc instead is stored in inst/doxygen, and during the build process, moved to inst/doc via a vignettes/Makefile. Packages with their own such Makefile will not have it overwritten, and developers may view the default Makefile provided by rdoxygen with the call

cat(readLines(system.file("sys", "Makefile",
                           package = "rdoxygen")), sep = "\n")