carchitectureembeddedcode-organization

How to organize code to make it platform agnostic (portable) part 2


I was forced to split my question to make it narrowed. The first part of the question.

Suppose I have:

/src
-- /component
-- -- component.c
-- -- component.h
-- main.c

Where the component implements some algorithm (which is platform agnostic) but uses some platform specific values (e.g. io pin number). main.c is platform specific code.

How to remove some debug code from production builds. For example in the component code I want to have debug logging. It should log when the application is built in debug mode and should disappear when in production mode. Therefore the component has a dependency on log function. We could make it explicit - declare a function prototype which the component accepts (and a client should pass). And call this function where needed:

// component.c
component_dbg_log(/* args */);

This function (passed by a client) could do nothing when in production mode or the client could pass NULL (then we need to make a check in the component code before calling it). In this case debug logic stays in the code in both modes (nothing is optimized). I believe it's not the idiomatic C style of coding.

Usual approach I've seen is to define some macro:

// the contents of define varies in different answers
#if DEBUG == 1
#define DEBUG_LOG(x) some_log_func(x)
#else
#define DEBUG_LOG(x)
#endif

// component.c
DEBUG_LOG("debug me");

The question is where should this macro go to? If I put it to a debug.h than my component is coupled with this debug.h file. If I want to make the component reusable should I provide also debug.h (what will be contained in the distribution pack/bundle)? What should another developer provide to the component code to make it work on his platform? If different components define its own debug macros wouldn't it be a mess?


Solution

  • To decouple the components from a debug.h header, you can do something like:

    // component.h
    /* this block will appear in any component that may use debugging */
    #ifndef DEBUG_LOG
    # define DEBUG_LOG(x) 
    #endif
    
    // debug.h
    #if defined (DEBUG_LOG)
    # undef DEBUG_LOG
    #endif
    #if DEBUG == 1
    # define DEBUG_LOG(x) some_log_func(x)
    #else
    # define DEBUG_LOG(x)
    #endif
    
    // component.c
    ...
    #if DEBUG == 1
    # include "debug.h"
    #endif
    ...
    

    Note that it doesn't matter what order the header files are included, DEBUG_LOG will be defined correctly when debugging is on, and whether or not the debug.h file is included.