Как временно отключить расширение макроса в C / C ++? - PullRequest
37 голосов
/ 09 октября 2009

По какой-то причине мне нужно временно отключить некоторые макросы в заголовочном файле, и #undef MACRONAME заставит код скомпилироваться, но он не найдет существующий макрос.

Есть ли способ просто отключить его?

Я должен упомянуть, что вы на самом деле не знаете значений макросов и что я ищу кросс-компиляторное решение (должно работать как минимум в GCC и MSVC).

Ответы [ 5 ]

82 голосов
/ 09 октября 2009

В MSVC вы можете использовать push_macro прагму, GCC поддерживает это для совместимости с компиляторами Microsoft Windows.

#pragma push_macro("MACRONAME")
#undef MACRONAME

// some actions

#pragma pop_macro("MACRONAME")
27 голосов
/ 09 октября 2009

Используя только те средства, которые определены в Стандарте C (C89, C99 или C11), единственный механизм «отключения» - это #undef.

Проблема в том, что нет механизма повторного включения.


Как уже отмечали другие, если файл заголовка, содержащий определения макросов, структурирован таким образом, что он не содержит никаких объявлений typedef или enum (их нельзя повторять; объявления функций и переменных могут повторяться), тогда вы могли бы #undef макрос, делайте то, что вам нужно, без действующего макроса, а затем повторно включите заголовок, возможно, после отмены определения его защиты от повторного включения.

Если макросы не определены в заголовке, конечно, вы застряли, пока не выполните рефакторинг кода, чтобы они были в заголовке.

Доступен еще один прием - если макросы являются функциональными макросами, а не объектными макросами.

#define nonsense(a, b)   b /\= a

int (nonsense)(int a, int b)
{
    return (a > b) ? a : b;
}

Функция nonsense() определена нормально, несмотря на макрос непосредственно перед ней. Это связано с тем, что за вызовом макроса (для функционально-подобного макроса) должна немедленно следовать открывающая скобка (дать или взять пробел, возможно, включая комментарии). В строке определения функции токен после «ерунды» является закрывающей скобкой, поэтому он не является вызовом макроса nonsense.

Если бы макрос был объектно-подобным макросом без аргументов, трюк не сработал бы:

#define nonsense min

int (nonsense)(int a, int b)
{
    // Think about it - what is the function really called?
    return (a > b) ? a : b;
}

Этот код определяет фиктивную функцию, которая называется min и является бессмысленной. И от макроса нет защиты.

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

2 голосов
/ 09 октября 2009

Макросы делают мои колени слабыми, но разве не было бы наиболее универсальным решением реструктурировать ваш код, чтобы вам не пришлось повторно включать макрос в том же исходном файле? Разве нельзя было бы извлечь какой-то код в отдельную функцию и отдельный исходный файл, в котором вы можете отменить ошибочный макрос.

1 голос
/ 09 октября 2009

Макросы происходят из некоторого заголовочного файла, поэтому вы должны иметь доступ к их значениям. Затем вы можете сделать что-то вроде

#include <foo.h> // declares macro FOO

// Do things with FOO

#undef FOO

// do things without FOO

#include <foo.h> // reenable FOO

Ваш заголовок должен быть оформлен в соответствии с этим

#ifndef FOO
#define FOO do_something(x,y)
#endif
0 голосов
/ 09 октября 2009

EDIT:

Вы можете подумать, что это так просто:

#ifdef macro
#define DISABLED_macro macro
#undef macro
#endif

// do what you want with macro

#ifdef DISABLED_macro
#define macro DISABLED_macro
#endif

Но это не так (как показано в следующем примере)!

#include <iostream>
#include <limits>

#include <windows.h>

#ifdef max
#define DISABLED_max max
#undef max
#endif

int main()
{
    std::cout << std::numeric_limits<unsigned long>::max() << std::endl;

#ifdef DISABLED_max
#define max DISABLED_max
#endif

    std::cout << max(15,3) << std::endl;  // error C3861: "max": identifier not found
    return 0;
}

Использование #undef в макросе и повторное включение исходного заголовка также вряд ли будет работать из-за защиты заголовков. Так что осталось использовать директивы push_macro/pop_macro #pragma.

#pragma push_macro("MACRO")
#undef MACRO
// do what you want
#pragma pop_macro("MACR")
...