Search code examples
cmacrosvariadic-macros

Macro shadowing both function and its address


I am working on a library in C. I have a function which should be available for the user of the library. It should be compiled and executed when a certain value is defined as 1, but it should not be compiled at all when this value is defined as 0 (usages should be replaced by (void)0 which will be optimized away).

I figured out, that I can use a macro for that, like here:

#include <stdio.h>
#include <stdarg.h>

#define ENABLE_PRINT_NUMBERS 0

#if ENABLE_PRINT_NUMBERS
void print_numbers(int number, ...);
#endif

#if !ENABLE_PRINT_NUMBERS
#define print_numbers(number, ...) (void)0
#endif

#if ENABLE_PRINT_NUMBERS
void print_numbers(int number, ...) {
    va_list va;
    va_start(va, number);
    const int second_number = va_arg(va, int);
    va_end(va);

    printf("numbers: %d, %d\n", number, second_number);
}
#endif

int main() {
    printf("## Shadowing by macro - test ##\n\n");

    print_numbers(1, 2);

    printf("function address: 0x%x\n", (void*)&print_numbers);

    return 0;
}

However, when such a function is not compiled (#define ENABLE_PRINT_NUMBERS 1), &print_numbers is not declared. Is there a way to write a macro such that both usages and function's address will be declared? Writing a second define with only function's name results in print_numbers previously defined here error.

I want to avoid "special empty functions", because there are like 15 other functions in this library that need to be cut out in release. This library is intended for embedded devices and I want to have as small memory footprint as possible.

To be clear:
I don't want to get the address of function-like macro. I want the address of this function to have SOME value (preferably null), because address of this function is used in many places in a library and I'm trying to modify the library as little as possible. Places where such an address is used have checks whether pointer is null, so it will not cause undefined behavior.


Solution

  • What you need is a function pointer instead of a function-like macro which you can initialize to a NULL pointer if print_numbers isn't available / ENABLE_PRINT_NUMBERS is 0:

    #include <stdio.h>
    #include <stdarg.h>
    
    #define ENABLE_PRINT_NUMBERS 1
    
    #if ENABLE_PRINT_NUMBERS
    void print_numbers(int number, ...);
    #endif
    
    int main (void) {
    
        void (*a)(int, ...);
    
        #if ENABLE_PRINT_NUMBERS
        a = &print_numbers; 
        #else
        a = 0;
        #endif
    
        printf("## Shadowing by macro - test ##\n\n");
    
        #if ENABLE_PRINT_NUMBERS
    
        print_numbers(1, 2);
    
        printf("function address: ");
    
        unsigned char *p = (unsigned char *)&a;
        size_t i;
    
        for (i = 0; i < sizeof a; i++)
        {
            printf("%02x ", p[i]);
        }
    
        putchar('\n');
    
        #endif
    
        return 0;
    }
    
    #if ENABLE_PRINT_NUMBERS
    void print_numbers(int number, ...) {
        va_list va;
        va_start(va, number);
        const int second_number = va_arg(va, int);
        va_end(va);
    
        printf("numbers: %d, %d\n", number, second_number);
    }
    #endif
    

    Side Notes:

    • A function-like macro doesn't have an address in memory. Macros are expanded by the preprocessor before compilation. Any attempt to gain or even print an address of it is a C syntax violation.

    • printf("function address: 0x%x\n", (void*)&print_numbers); to print a function address is wrong. The x conversion specifier expects an argument of type unsigned int and the C standard does not allow you to cast a function pointer to a void pointer.

      I actually found out that it is almost impossible to print a function address instead of one approach, I've found here, using the conversion from the pointer to the function to an unsigned char pointer and some trickery at printing. I used that method.