You are here

Naming conventions and coding style guide

Primary tabs

Here you can find some rules to help you write code for GNUnet.

Naming conventions

include files

  • _lib: library without need for a process
  • _service: library that needs a service process
  • _plugin: plugin definition
  • _protocol: structs used in network protocol
  • exceptions:
    • gnunet_config.h --- generated
    • platform.h --- first included
    • plibc.h --- external library
    • gnunet_common.h --- fundamental routines
    • gnunet_directories.h --- generated
    • gettext.h --- external library

binaries

  • gnunet-service-xxx: service process (has listen socket)
  • gnunet-daemon-xxx: daemon process (no listen socket)
  • gnunet-helper-xxx[-yyy]: SUID helper for module xxx
  • gnunet-yyy: command-line tool for end-users
  • libgnunet_plugin_xxx_yyy.so: plugin for API xxx
  • libgnunetxxx.so: library for API xxx

logging

  • services and daemons use their directory name in GNUNET_log_setup (i.e. 'core') and log using plain 'GNUNET_log'.
  • command-line tools use their full name in GNUNET_log_setup (i.e. 'gnunet-publish') and log using plain 'GNUNET_log'.
  • service access libraries log using 'GNUNET_log_from' and use 'DIRNAME-api' for the component (i.e. 'core-api')
  • pure libraries (without associated service) use 'GNUNET_log_from' with the component set to their library name (without lib or '.so'), which should also be their directory name (i.e. 'nat')
  • plugins should use 'GNUNET_log_from' with the directory name and the plugin name combined to produce the component name (i.e. 'transport-tcp').
  • logging should be unified per-file by defining a LOG macro with the appropriate arguments, along these lines:
    #define LOG(kind,...) GNUNET_log_from (kind, "example-api",__VA_ARGS__)

configuration

  • paths (that are substituted in all filenames) are in PATHS (have as few as possible)
  • all options for a particular module (src/MODULE) are under [MODULE]
  • options for a plugin of a module are under [MODULE-PLUGINNAME]

exported symbols

  • must start with "GNUNET_modulename_" and be defined in "modulename.c"
  • exceptions: those defined in gnunet_common.h

private (library-internal) symbols (including structs and macros)

  • must NOT start with any prefix
  • must not be exported in a way that linkers could use them or
    other libraries might see them via headers; they must be either
    declared/defined in C source files or in headers that are in
    the respective directory under src/modulename/ and NEVER be
    declared in src/include/.

testcases

  • must be called "test_module-under-test_case-description.c"
  • "case-description" maybe omitted if there is only one test

performance tests

  • must be called "perf_module-under-test_case-description.c"
  • "case-description" maybe omitted if there is only one performance test
  • Must only be run if HAVE_BENCHMARKS is satisfied

src/ directories

  • gnunet-NAME: end-user applications (i.e., gnunet-search, gnunet-arm)
  • gnunet-service-NAME: service processes with accessor library (i.e., gnunet-service-arm)
  • libgnunetNAME: accessor library (_service.h-header) or standalone library (_lib.h-header)
  • gnunet-daemon-NAME: daemon process without accessor library (i.e., gnunet-daemon-hostlist) and no GNUnet management port
  • libgnunet_plugin_DIR_NAME: loadable plugins (i.e., libgnunet_plugin_transport_tcp)

Coding style

  • GNU guidelines generally apply
  • Indentation is done with spaces, two per level, no tabs
  • C99 struct initialization is fine
  • declare only one variable per line, so
      int i;
      int j;
    

    instead of

      int i,j;
    

    This helps keep diffs small and forces developers to think precisely about the type of every variable. Note that char * is different from const char* and int is different from unsigned int or uint32_t. Each variable type should be chosen with care.

  • While goto should generally be avoided, having a goto to the end of a function to a block of clean up statements (free, close, etc.) can be acceptable.
  • Conditions should be written with constants on the left (to avoid accidental assignment) and with the 'true' target being either the 'error' case or the significantly simpler continuation. For example:
     if (0 != stat ("filename," &sbuf)) 
    { 
      error(); 
    } 
    else
    { 
      /* handle normal case here */
    }
    

    instead of

     if (stat ("filename," &sbuf) == 0) 
    { 
      /* handle normal case here */
    } 
    else
    { 
      error(); 
    }
    

    If possible, the error clause should be terminated with a 'return' (or 'goto' to some cleanup routine) and in this case, the 'else' clause should be omitted:

     if (0 != stat ("filename," &sbuf)) 
    { 
      error(); 
      return;
    } 
    /* handle normal case here */
    

    This serves to avoid deep nesting. The 'constants on the left' rule applies to all constants (including. GNUNET_SCHEDULER_NO_TASK), NULL, and enums). With the two above rules (constants on left, errors in 'true' branch), there is only one way to write most branches correctly.

  • Combined assignments and tests are allowed if they do not hinder code clarity. For example, one can write:
     if (NULL == (value = lookup_function()))
    { 
      error(); 
      return;
    } 
    
  • Use break and continue wherever possible to avoid deep(er) nesting. Thus, we would write:
    next = head;
    while (NULL != (pos = next))
    { 
      next = pos->next;
      if (! should_free (pos))
         continue; 
      GNUNET_CONTAINER_DLL_remove (head, tail, pos);
      GNUNET_free (pos);
    } 
    

    instead of

    next = head;
    while (NULL != (pos = next))
    { 
      next = pos->next;
      if (should_free (pos))
      {
        /* unnecessary nesting! */
        GNUNET_CONTAINER_DLL_remove (head, tail, pos);
        GNUNET_free (pos);
      }
    } 
    
  • We primarily use for and while loops. A while loop is used if the method for advancing in the loop is not a straightforward increment operation. In particular, we use:
    next = head;
    while (NULL != (pos = next))
    { 
      next = pos->next;
      if (! should_free (pos))
         continue; 
      GNUNET_CONTAINER_DLL_remove (head, tail, pos);
      GNUNET_free (pos);
    } 
    

    to free entries in a list (as the iteration changes the structure of the list due to the free; the equivalent for loop does no longer follow the simple for paradigm of for(INIT;TEST;INC)). However, for loops that do follow the simple for paradigm we do use for, even if it involves linked lists:

    /* simple iteration over a linked list */
    for (pos = head; NULL != pos; pos = pos->next)
    { 
       use (pos);
    } 
    
  • The first argument to all higher-order functions in GNUnet must be declared to be of type void * and is reserved for a closure. We do not use inner functions, as trampolines would conflict with setups that use non-executable stacks.
    The first statement in a higher-order function, which unusually should be part of the variable declarations, should assign the cls argument to the precise expected type. For example:
    int
    callback (void *cls, char *args)
    { 
       struct Foo *foo = cls;
       int other_variables;
    
       /* rest of function */
    } 
    
  • It is good practice to write complex if expressions instead of using deeply nested if statements. However, except for addition and multiplication, all operators should use parens. This is fine:
    if ( (1 == foo) || 
         ((0 == bar) && (x != y)) )
      return x;
    

    However, this is not:

    if (1 == foo) 
      return x;
    if (0 == bar && x != y)
      return x;
    

    Note that splitting the if statement above is debateable as the return x is a very trivial statement. However, once the logic after the branch becomes more complicated (and is still identical), the "or" formulation should be used for sure.

  • There should be two empty lines between the end of the function and the comments describing the following function. There should be a single empty line after the initial variable declarations of a function. If a function has no local variables, there should be no initial empty line. If a long function consists of several complex steps, those steps might be separated by an empty line (possibly followed by a comment describing the following step). The code should not contain empty lines in arbitrary places; if in doubt, it is likely better to NOT have an empty line (this way, more code will fit on the screen).

Build-system

If you have code that is likely not to compile or build rules you might want to not trigger for most developers, use "if HAVE_EXPERIMENTAL" in your Makefile.am. Then it is OK to (temporarily) add non-compiling (or known-to-not-port) code.

If you want to compile all testcases but NOT run them, run configure with the
--enable-test-suppression option.

If you want to run all testcases, including those that take a while, run configure with the
--enable-expensive-testcases option.

If you want to compile and run benchmarks, run configure with the
--enable-benchmarks option.

If you want to obtain code coverage results, run configure with the
--enable-coverage option and run the coverage.sh script in contrib/.