Table of contents
    No headers

    Encapsulation is a term used in object oriented programming (e.g. C++, Java) to indicate that data and algorithms are separated in a computer program. This can be enforced in C as well, and we recommend that all new code is made according to the following standard:

    • Data structures should be hidden in the implementation part of the code by making them abstract
    • Derived code should not modify fields of data structures directly (and this is enforced by abstract data types), but rather use functions
    • All functions should return some number or pointer to check the result
    • All data structures should at least have an init routine to initiate the data structure and a done (or destroy) routine to free memory etc.

    To make this somewhat more tangible here is an example, of an algorithm to harvest apples from a tree.

    /* gmx_apple.h */
    /* Abstract type defines a pointer to a structure */
    typedef gmx_apple *
    /* Initiate the orchard. This routine takes a pointer to an abstract, uninitialized, apple
      * datatype (which internally is a pointer-to-a-pointer, but that is none of the users business!),
      * and tries to initialize it, e.g. by allocating memory and settings stuff in the contents.
      * The return value tells us if the initialization was successful (0), or if something happened.
      * By using the return value for an error code rather than the data type we can check what went wrong.
    gmx_apple_init(gmx_apple_t *apple);
    /* Get the harvest in kilograms for this year. Return 0 if OK, error code in case of problems */
    gmx_apple_get_harvest(gmx_apple_t apple,double *harvest);
    /* Set the amount of precipitation (mm/year). Return 0 if OK, error code otherwise */
    gmx_apple_set_precipitation(gmx_apple_t apple,double precipitation);
    /* Obliterate the orchard. Typically destructors do not return anything since we cannot do much
      * when/if they fail (what if the contents was partially destructed?). 
    gmx_apple_done(gmx_apple_t apple);

    A test program would look like this:

    /* gmx_apple_test.c */
    #include <stdio.h>
    #include "gmx_apple.h"
    int main(int argc,char *argv[])
        gmx_apple_t apple;
        double      harvest;
        int         rc;
        rc = gmx_apple_init(&apple); 
        /* handy trick - put constants first in comparisons! if you make a typo and turn this 
          * into an assignment, the compiler won't accept assigning to a constant value.
          * Second, it is better to have short if-statements with a separate return statement
          * rather than a huge if-statement spanning dozens of lines and increasing indentation.
        if( 0 != rc )
              printf("No orchard could be established on this barren soil.\n"); 
              return rc;
       /* Note that we have hard-coded the precipation level for this year. 3 points deducted. */
       rc = gmx_apple_set_precipitation(apple,130.5);
       if ( 0 == rc ) 
           rc = gmx_apple_get_harvest(apple,&harvest);
       if ( 0 != rc )
          printf("A storm prevent harvesting the otherwise healthy tree. Error code was %d\n",code);
          printf("The harvest was %lf kg of juicy apples this year\n",harvest);
       rc = gmx_apple_done(apple);
       if ( 0 != rc ) 
           printf("Can not even get rid of the orchard. Error code was %d\",rc);
       return 0;

    Finally, the implementation of gmx_apple.c is left as an exercise to the reader. Since we have a good API, who cares about the implementation? And, more important, next year when we plan to have our SSE-optimized orchard ready all routines using this API will magically see an amazing increase in apple harvest!

    Page last modified 13:38, 21 Mar 2009 by rossen