Search code examples
cgccmacrosc-preprocessoravr

C preprocessor macro replacing #if #else statements


I'm using this statement now to switch between small and slightly more extensive error messages in my C code:

#ifdef COMPACTC
  error_code ((uint16_t) init_cascade, 5);
#else
  error_message (__func__, "cascades malloc failed");
#endif

(it's part of an embedded C code library, so I often don't have the memory available to store the error message strings).

In the first case (COMPACTC defined) I output only a function address and and error number. In the second case I output the full function name and a human-readable error message.

Since I'm copying this snippet all over my code now, I hope I can replace it with a single macro, which I can use like this:

error (init_cascade, "cascades malloc failed", 5)

and provides the correct C code depending on COMPACTC being defined or not.

Maybe it's even possible to have the current function (or some unique identifier) automatically derived from the current location, though I think the preprocessor doesn't know which function's scope it's currently in.

Another improvement would be if a unique error code (5 in this case, which is based on the return value) could be automatically generated from the error message (like a hash value or something).

Then the macro call could be as simple as this:

error ("cascades malloc failed")

Of course, when using hashes, I would need some sort of reference (file/comment) to its original message.

Context: error messages on AVR 8-bits MCU

About the context of this question: This code is written for the ATmega168 (having only 2k of RAM), and AFAIK there's no good/recent emulator available for Linux. So I'm trying to find a good way to produce error messages/codes without the strings eating all of my memory.

Alternative: C function?

A completely different, probably more flexible solution would be to define a C function error (function, message, code) and move the #if/#else/#endif construction to its body. I'm not sure though whether a string literal is compiled in by gcc if it is referred to in a function call but never used inside the function body.

Edit: I just tested this idea: using a string literal in a call but not in the body still has it added to the executable by gcc, so this "alternative" doesn't seem to work.


Solution

  • This should actually be fairly easy:

    #ifdef COMPACTC
    #   define COMBINED_ERROR(function, message, size) error_code((uint16_t)function, size)
    #else
    #   define COMBINED_ERROR(function, message, size) error_message (__func__, message)
    #endif
    

    and now you can write:

    COMBINED_ERROR(init_cascade, "cascades malloc failed", 5);