Chapter 12. Contributing to Regina

This page serves as a checklist for adding new code to Regina.

  • Regina is written in C++, and so any permanent contributions to Regina will need to be written in C++ also.

  • New classes should use value semantics, unless there is a good reason not to. Classes with value semantics should provide appropriate comparisions (== and !=), copy constructors, and copy assignment operators. If they are large objects, they should also provide move constructors, move assignment operators, a member swap() function, and a global swap() function.

  • New code should ideally come with corresponding tests for Regina's test suite:

    • For important classes, these tests should be comprehensive, not just a few token cases. See the Integer tests in testsuite/maths/integer.cpp for an example of very comprehensive testing.
    • The tests may be either C++ (in testsuite/) or Python (in python/testsuite/), or both. Python tests are simpler to write; however, the C++ tests can be far more flexible in what is tested (not just comparing text output), and moreover the C++ tests will be run on more systems (including those where Python development files are not available).
    • Whilst they should be comprehensive, tests should not be enormously slow. Watch how fast/slow the other tests are as a guide for what is acceptable (if you test using “make test ARGS=-V” then you can see each individual test as it runs).

  • All functions and classes should be fully documented. In particular, for each function:

    • The first sentence of the documentation should be appropriate to display as a brief synopsis (since this will be extracted and placed into various indices).
    • The documentation should list all preconditions that the user needs to ensure before calling the function.
    • Functions should not be correct only for “common” scenarios, but should be as general as possible within reason. Any limitations should be made clear to the user (e.g., through explicit preconditions)—users should not be expected to guess which scenarios the function works for.
    • All parameters should be described in individual \param blocks. Likewise, any non-type template parameters (e.g., integer template parameters) must be described in \tparam blocks. Of course, type parameters may be described also if this will be helpful. The return value should be described in a \return block.
    • All preconditions that the user is expected to adhere to should be spelled out in the documentation. Limits on function arguments (e.g., acceptable integer ranges) can be listed in each \param block; other preconditions should be listed in separate \pre paragraphs.
    • If there are differences between the C++ and Python versions of a function or class, these should be listed in a separate \python paragraph. In particular, if function arguments differ (e.g., the C++ version takes a pair of iterators but the Python version takes a single list), then the arguments should be named in the Python bindings using pybind11::arg(...). If there is no Python version at all, this should be made clear using a \nopython tag (which can take an optional explanation).

  • Documentation needs to be written in a format that is easy for humans to read in the C++ headers, will be formatted appropriately when processed by Doxygen, and will convert sensibly to a Python docstring. Best practices include:

    • Avoid HTML tags such as <i>, <b>, and <tt> where possible—instead use markdown and/or Doxygen tags. In particular: for italicised English, use _underscores_; for italicised variable names use \a; for bold English use **double asterisks**; for C++ keywords use \c; and for inline code fragments use `backticks`. Note that Doxygen does not (currently) translate underscores within \exception blocks; here you will need to italicise using \e (for single words) or <i> (for multiple words).
    • Avoid XML entities such as &lt;, &amp;, and &ne;. For less-than, you can use \< if there is a risk of Doxygen misinterpreting a plain < as an HTML tag. For mathematical symbols with “simple” unicode representations (i.e., likely to be supported in fonts across many platforms), just use the unicode instead. Simple unicode symbols include: ≤≥≠, ×±, αβπ, and ¹²³.
    • Use Doxygen-style backslash tags such as \param, not Javadoc-style tags such as @param.

  • All C++ functions and classes should have corresponding Python bindings, unless there is a very good reason not to. The Python bindings are typically easy to write, and Ben can help with this if necessary.

    • Template classes should be wrapped in Python using suffixes that describe the template arguments. See for example Triangulation3, or TreeEnumeration_NonSpun.
    • All functions and classes wrapped in Python should have appropriate docstrings, which need to be passed to the corresponding pybind11::def(...) statement. These docstrings are extracted automatically from the hand-written documentation in the C++ headers, and this extraction is done manually (not as part of the build process). Please ask Ben if you need this extraction to be run for you.
    • If for some reason you are binding a function in Python but you do not want to extract a docstring (this should be a rare occurrence), use a \nodocstring tag in the C++ class or function documentation. (At the time of writing, \nodocstring is used in only two places across Regina's entire API.)