Как отключить макросы, импортированные из C-заголовка

Класс A использует библиотеку, написанную в C. Эта библиотека предоставляет некоторые типы данных и константы, которые используются в A. К сожалению, библиотека также определяет макросы в своем заголовочном файле, которые сталкиваются с моим кодом на С++ в main.cpp или в других классах с использованием A.

Как я могу предотвратить выполнение макросов c_library.h, когда A.h где-то включен? Я также был бы открыт для архитектурных изменений, но я бы предпочел не прикасаться к библиотеке C.

Конечно, есть директива #undef. Но это означало бы большую ручную работу для каждого макроса или для каждого столкновения. (Хорошо, их не так уж много, но эй, это должно быть возможно более элегантно?)

код:

//main.cpp

#include "A.h"

...
A a(...)
...
std::max(x, y); // oops, problem since max is defined as macro in c_library.h
...

//A.h
#include "c_library.h"

class A{
public:
    A(...);
    static void callbackForCLibrary(datatypeOfCLibrary d){...}
private:
    private datatypeOfCLibrary1;
    private datatypeOfCLibrary2;
}

Ответ 1

Вы уже знаете о параметре #undef, который будет делать то, что вам нужно.

Однако есть и другой вариант. Вы можете полностью скрыть тот факт, что ваш A использует библиотеку C у ваших пользователей: определите свои собственные типы и интерфейс в определении заголовка и класса A и удалите библиотеку из заголовка A. Затем в вашем файле реализации вы можете включить заголовок библиотеки и использовать библиотеку любым способом, все время скрывая включение c_library.h от ваших пользователей. Это дает дополнительное преимущество в уменьшении связи между пользователями вашего класса, вашим классом и библиотекой, от которой оно зависит.

Ответ 2

Вы можете создать "wrap_c_library.h", что-то вроде:

#ifndef WRAP_C_LIBRARY_H
#define WRAP_C_LIBRARY_H

#include "c_library.h"

#undef TROUBLESOME_MACRO_FROM_C_LIBRARY

#endif // WRAP_C_LIBRARY_H

Ответ 3

Потенциально (не уверен, насколько хорошо это будет работать на практике) #undef не обязательно означает ручную работу - вы можете автоматически сгенерировать второй файл, включив в него #undef все определения из первого заголовка.

например. с учетом этого заголовка:

#define A(X, Y) [X ; Y]
#define B(X, Y) {X...Y}
#define C this is C
#define D this is D

... запустите следующий короткий script:

gcc -undef -dN -E foo.h > undef_foo.h
sed -i ".bak" 's/#define[ \t]\([A-Za-z0-9_]*\)/#undef \1/g' undef_foo.h
gcc -undef -dD -E - < /dev/null >> undef_foo.h
sed -i ".bak" '/#[du]/!d' undef_foo.h

... для создания этого встречного заголовка:

#undef __STDC__
#undef __STDC_HOSTED__
#undef __DYNAMIC__
#undef A
#undef B
#undef C
#undef D
#define __STDC__ 1
#define __STDC_HOSTED__ 1
#define __DYNAMIC__ 1

Основная идея: получить список всех определений, которые являются следствием включения foo.h. Параметры -undef -dN для GCC минимизируют количество предоставленных системой материалов, которые будут включены в этот список (до трех для меня, не уверен, насколько это согласовано), чтобы минимизировать залог и упростить вывод. Затем замените все строки #define на эквивалентные строки #undef (-dN делает это проще, не перечисляя замены). Затем добавьте все макроопределенные макросы, которые GCC по-прежнему включается в конец файла, поэтому их значения восстанавливаются. Наконец, удалите все директивы из файла, которые не являются #define или #undef.

Использование:

#include "foo.h"
#include "undef_foo.h"

A(1, 2)
B(3, 4)
C
D

Запустите gcc -E и наблюдайте, как макросы не расширяются.

Кто-то с лучшими навыками написания сценариев, вероятно, может сделать это намного лучше, но это основная идея.

Ответ 4

Вы можете использовать директивы препроцессора #ifdef и #undef для обнаружения функций, которые вы хотите вызывать на С++, и отключить встречные макросы, объявленные в C-заголовке

Ответ 5

1) Для любого конкретного макроса вы можете "отключить" его с помощью #undef.

2) Если вам не нужны ЛЮБЫЕ определения из определенного заголовка: просто не #include это.

3) Если заголовок неявно включен из чего-то другого, и вы все же хотите "отключить" весь заголовок, вы можете #define защитить заголовок перед первым включением.

4) Я не могу представить, чтобы какой-либо из этих параметров применим к "std:: max()":

http://en.cppreference.com/w/cpp/algorithm/max

Ответ 6

Другой подход заключается в добавлении нового макропереключателя в проблемный файл "DO_NOT_DEFINE_MACROS"

Измените проблемный файл, чтобы НЕ определять макросы, если этот макрос определен,

Затем, прежде чем включить его, определите макрос, включите файл, затем отмените определение макроса.