Макросъемка как функции, так и ее адреса - PullRequest
1 голос
/ 17 июня 2020

Я работаю над библиотекой в ​​C. У меня есть функция, которая должна быть доступна пользователю библиотеки. Он должен быть скомпилирован и выполнен, когда определенное значение определено как 1, но он не должен компилироваться вообще, когда это значение определено как 0 (использование должно быть заменено на (void)0, которое будет оптимизировано).

Я понял, что могу использовать для этого макрос, как здесь:

#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;
}

Однако, когда такая функция не компилируется (#define ENABLE_PRINT_NUMBERS 1), &print_numbers не объявляется. Есть ли способ написать макрос, в котором будут объявляться и использование, и адрес функции? Написание второго определения только с именем функции приводит к ошибке print_numbers previously defined here.

Я хочу избежать использования «специальных пустых функций», потому что в этой библиотеке есть еще 15 функций, которые необходимо исключить в выпуске. Эта библиотека предназначена для встраиваемых устройств, и я хочу иметь как можно меньший объем памяти.

Для ясности:
Я не хочу получать адрес функционального макроса. Я хочу, чтобы адрес этой функции имел НЕКОТОРЫЕ значение (желательно ноль), потому что адрес этой функции используется во многих местах библиотеки, и я стараюсь как можно меньше изменять библиотеку. В местах, где используется такой адрес, проверяется, является ли указатель нулевым, поэтому это не вызовет неопределенного поведения.

Ответы [ 3 ]

3 голосов
/ 17 июня 2020

Что вам нужно, так это указатель на функцию вместо макроса, подобного функции, который вы можете инициализировать указателем NULL, если print_numbers недоступен / ENABLE_PRINT_NUMBERS равно 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

Примечания:

  • Функциональный макрос не имеет адреса в памяти. Макросы расширяются препроцессором перед компиляцией. Любая попытка получить или даже распечатать его адрес является нарушением синтаксиса C.

  • printf("function address: 0x%x\n", (void*)&print_numbers); выводить адрес функции неверно. Спецификатор преобразования x ожидает аргумент типа unsigned int, а стандарт C не позволяет преобразовать указатель функции на указатель void.

    Я действительно обнаружил, что это почти невозможно напечатать адрес функции вместо одного подхода, я нашел здесь , используя преобразование указателя на функцию в указатель unsigned char и некоторые уловки при печати. Я использовал этот метод.

1 голос
/ 17 июня 2020

Вы подходите к этому неверно.

Код приложения должен проверять ENABLE_PRINT_NUMBERS, чтобы узнать, вызывать ли рассматриваемую функцию или нет.

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;
}

Это общепринятый метод проверки кода приложения на наличие определенных функций в разных версиях библиотеки.

0 голосов
/ 17 июня 2020

Макросы обрабатываются перед компиляцией (это называется предварительной обработкой). Таким образом, часть кода, которая находится в блоке #if при условии false, не существует при запуске компиляции.

Результирующий файл C, переданный фактическому C компилятору:

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

Макросы текстовые ( это упрощение, но оно достаточно хорошо для объема этого вопроса) заменено во время предварительной обработки.

Нет определения функции, поэтому нет ее адреса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...