Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

Doxygen standards

Bruno Bottazzini edited this page Jun 10, 2016 · 12 revisions

Doxygen standards

All public C API must be documented with Doxygen.

To use it some commands must be written on comment blocks. Most used commands on Soletta™ Framework's documentation are:

  • @brief to describe functions, structs, sections
  • @param to talk about each function parameter
  • @return to explain what user should expect when calling a function
  • @see and @ref to reference related documentation
  • @li for list items
  • @code and @endcode to include snippets
  • @c to use typewriter font - to refer word of code
  • @note to add extra notes users should learn about

For a complete list of all commands available please refer to the official documentation

Doxygen supports many ways to write docs, so we'll try to follow some standards for our project, exemplified below

Macros must be documented too

If you're facing issues, check Known Issues sections with some workaround to avoid what it seems to be doxygen issues.

Functions

How you should write a function documentation:

  • @brief with a very small description to be listed together with the function signature. Otherwise everything only will be displayed on "Detailed Description" section, reducing documentation readability.
  • More details in extra paragraphs
  • Eventual notes should be done with @note
  • Extra function references for the reader can be made with @see
  • If parameters must be referenced, use command @a
  • If something else must be referenced, like structs, use @ref command.
  • For values, use @c so it'll use typewriter font
  • Add @param for each parameter. On same cases may be useful to highlight values that can or cannot be passed, like NULL, etc
  • Finally, add a @return command if function isn't void. If memory must be freed for the user, you must tell her here. If other specific functions must be used too.

E.g.:

/**
 * @brief Opens a given pin by its board label as general purpose input or output.
 *
 * This function only works when the board was successfully detected
 * by Soletta and a corresponding
 * pin multiplexer module was found.
 *
 * @note A pin defined by @a label should be opened just once, calling this function more than once
 * for the same pin results in undefined behavior - per platform basis.
 *
 * @see sol_gpio_open_raw(), sol_gpio_close().
 *
 * @param label The pin to be opened.
 * @param config Contains the pin configuration.
 *
 * @return A new @ref sol_gpio instance on success, @c NULL otherwise.
 */
struct sol_gpio *sol_gpio_open_by_label(const char *label, const struct sol_gpio_config *config) SOL_ATTR_WARN_UNUSED_RESULT;

Structs and enums

Fields can be documented inline (with /**< ... */) or above (specially useful for complex stuff).

Remember to keep a @brief with a very small description to be listed together with the struct / enum in any case. Otherwise everything only will be displayed on "Detailed Description" section, reducing documentation readability.

Apparently doxygen has a hard time handling opaque structs.

It seems to not even list them. So if you want to make sure documentation will be displayed, it's required to add a @struct command with struct name:

/**
 * @struct sol_coap_packet
 *
 * @brief Opaque handler for a CoAP packet.
 *
 * [...]
 */

Inline:

/**
 * @brief Enum for I2C bus speed.
 *
 * Must be choosen when opening a bus with sol_i2c_open() and
 * sol_i2c_open_raw().
 */
enum sol_i2c_speed {
    SOL_I2C_SPEED_10KBIT = 0, /**< flag for low speed */
    SOL_I2C_SPEED_100KBIT, /**< flag for normal speed */
    SOL_I2C_SPEED_400KBIT, /**< flag for fast speed */
    SOL_I2C_SPEED_1MBIT, /**< flag for fast plus speed */
    SOL_I2C_SPEED_3MBIT_400KBIT /**< flag for high speed */
};

Above:

/**
 * @brief Structure to hold the configuration of a GPIO device.
 *
 * When opening a GPIO with sol_gpio_open_by_label(), sol_gpio_open() or
 * sol_gpio_open_raw(), the parameters with which the GPIO is configured are
 * those defined in this structure.
 *
 * If there's a need to change any of these parameters, the GPIO must be closed
 * and opened again with a new configuration.
 */
struct sol_gpio_config {
    #ifndef SOL_NO_API_VERSION
    #define SOL_GPIO_CONFIG_API_VERSION (1)
        uint16_t api_version;
    #endif
        /**
         * The direction in which to open the GPIO.
         */
        enum sol_gpio_direction dir;
        /**
         * Whether the GPIO is considered active when it's in a low state.
         *
         * If set, then the logical state of the GPIO will be reversed in relation
         * to the physical state. That is, for input GPIOs, when the current on
         * the wire goes to a low state, the value returned by sol_gpio_read() will
         * be @c true. Conversely, it will be @c false when the physical state is
         * high.
         *
         * The same logic applies for output GPIOs when a value is written through
         * sol_gpio_write().
         *
         * This is useful to keep the application logic simpler in the face of
         * different hardware configurations.
         */
        bool active_low;
        /**
         * Pull-up or pull-down resistor state for this GPIO.
         *
         * One of #sol_gpio_drive. Some platforms will configure GPIO taking
         * this in consideration, as Continki and RIOT.
         */
        enum sol_gpio_drive drive_mode;
        union {
            /**
             * Configuration parameters for input GPIOs.
             */
            struct {
                /**
                 * When to trigger events for this GPIO.
                 *
                 * One of #sol_gpio_edge. If the value set is anything other
                 * than #SOL_GPIO_EDGE_NONE, then the @c cb member must be set.
                 */
                enum sol_gpio_edge trigger_mode;
                /**
                 * The function to call when an event happens.
                 *
                 * Different systems handle interruptions differently, and so to
                 * maintain consistency across them, there is no queue of values
                 * triggered by interruptions. Instead, when an interruption
                 * happens, the main loop will handle it and call the user function
                 * provided here, with the value of the GPIO at that time.
                 * This means that the if the application takes too long to return
                 * to the main loop while interruptions are happening, some of those
                 * values will be lost.
                 *
                 * @param data The user data pointer provided in @c user_data.
                 * @param gpio The GPIO instance that triggered the event.
                 * @param value The value of the GPIO at the moment the function
                 *              is called.
                 */
                void (*cb)(void *data, struct sol_gpio *gpio, bool value);

Groups

/**
 * @defgroup GPIO GPIO
 * @ingroup IO
 *
 * GPIO (General Purpose Input/Output) API for Soletta.
 *
 * @{
 */

Root groups must be added to our mainpage list at src/lib/common/include/sol-mainloop.h

E.g.:

/**
 * @mainpage Soletta Project Documentation
 *
 * [...]
 * For a better reference, check the following groups:
 * @li @ref Comms
 * @li @ref Datatypes
 * @li @ref Flow
 * @li @ref IO
 * [...]
 */

Files

/**
 * @file
 * @brief These routines are used for GPIO access under Soletta.
 */

Lists and references

/**
 * SML is divided in the following groups:
 *
 * @li @ref Engine
 * @li @ref Fuzzy_Engine
 * @li @ref Log
 * @li @ref Mainloop
 * @li @ref Ann_Engine
 * @li @ref Variables
 */

Code snippets

May be done by two different ways: referencing samples or writing code:

Reference:

/**
 * Choosing engines and creating variables is straight-forward after
 * [...]
 *
 * @dontinclude example_doc.c
 * @skip main
 * @until sml_set_output_state_changed_callback
 *
 * Terms are a way to split the values in the range in meaningful parts.
 * [...]
 *
 * @until }
 */
```C

Writing code:

```C
/**
 * Example to identify a request:
 * @code
 * struct sol_coap_packet *req = X;
 * if (sol_coap_header_get_code(req) & SOL_COAP_REQUEST_MASK)
 *     {
 *         // do something
 *     }
 * @endcode
 */

Known issues

Apparently doxygen gets confused by our macros to set required use of result or nonnull args.

For instance:

struct sol_http_client_connection *sol_http_client_request_with_interface(enum sol_http_method method,
    const char *url, const struct sol_http_params *params,
    const struct sol_http_request_interface *interface,
    const void *data) SOL_ATTR_NONNULL(2, 4) SOL_ATTR_WARN_UNUSED_RESULT;

will produce in documentation a variable struct sol_http_client_connection SOL_ATTR_WARN_UNUSED_RESULT

Changing macros order seems to avoid this issue:

struct sol_http_client_connection *sol_http_client_request_with_interface(enum sol_http_method method,
    const char *url, const struct sol_http_params *params,
    const struct sol_http_request_interface *interface,
    const void *data) SOL_ATTR_WARN_UNUSED_RESULT SOL_ATTR_NONNULL(2, 4);
Clone this wiki locally