1

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.

Dinuirar
  • 25
  • 5
  • I don't understand why would you attempt to gain and print the address of a function-like macro instead of a defined function? A macro is just a macro which is expanded before compilation. It doesn't have an address in memory. Any attempt to do so is a C syntax violation and the compiler should provide an error. – RobertS supports Monica Cellio Jun 17 '20 at 12:15
  • 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 as little as possible. – Dinuirar Jun 17 '20 at 12:18

3 Answers3

3

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.

Community
  • 1
  • 1
1

You're approaching this the wrong way.

The application code should be checking ENABLE_PRINT_NUMBERS to see whether or not to call the function in question.

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

#ifdef ENABLE_PRINT_NUMBERS
    print_numbers(1, 2);

    printf("function address: 0x%x\n", (void*)&print_numbers);
#else
    printf("print_numbers not defined, do something else instead\n");
#endif

    return 0;
}

This is the generally accepted method for application code to check the existence of particular features in different versions of a library.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

Macros are processed before the compilation (it is called preprocessing). So the part of the code which is in the #if block when condition is false do not exists when compilation starts.

The resulting C file passed to the actual C compiler is:

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

    (void)0;

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

    return 0;
}

https://godbolt.org/z/TBM3hp

Macros are textually (it is a simplification but it is good enough for the scope of this question) replaced during the preprocessing.

There is no function definition thus no address of it.

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Yes, I agree but what to do about this function pointer? I want it to be null, but it does not seem obvious how to achieve that with macros. – Dinuirar Jun 17 '20 at 12:02
  • The object with the name `print_numbers` does not exists. So how do you want to get its address? Do you see the definition of the `print_numbers` function somewhere in code after the preprocessing. – 0___________ Jun 17 '20 at 12:03
  • I added a clarification in my question. Thanks for the answer : ) – Dinuirar Jun 17 '20 at 12:32