Какие варианты использования требуют #define без строки токена? - PullRequest
6 голосов
/ 29 сентября 2011

Я встречал директиву препроцессора #define и раньше, когда изучал C, а затем столкнулся с ней в некотором коде, который я прочитал. Но кроме использования его для определенных подстановок констант и для определения макросов, я не особо занимался особым случаем, когда он используется без "body" или строки-токена.

Возьмем для примера эту строку:

#define OCSTR(X)

Просто так! Какая польза от этого или лучше, когда это использование #define необходимо?

Ответы [ 8 ]

7 голосов
/ 29 сентября 2011

Это используется в двух случаях.Первая и наиболее частая включает условную компиляцию:

#ifndef XYZ
#define XYZ
//  ...
#endif

Вы наверняка использовали это сами для включения защиты, но это также может быть использовано для таких вещей, как системные зависимости:

#ifdef WIN32
//  Windows specific code here...
#endif

(В этом случае WIN32 более вероятно определяется в командной строке, но он также может быть определен в файле "config.hpp".) Это обычно включает только объектоподобные макросы (без списка аргументов или скобок).

Второй будет результатом условной компиляции.Что-то вроде:

#ifdef DEBUG
#define TEST(X) text(X)
#else
#define TEST(X)
#endif

Это позволяет писать такие вещи как:

TEST(X);

, которые будут вызывать функцию, если определено DEBUG, и ничего не делать, если это не так.

3 голосов
/ 29 сентября 2011

Такой макрос обычно появляется в паре и внутри условного #ifdef как:

#ifdef _DEBUG
   #define OCSTR(X)
#else
   #define OCSTR(X)  SOME_TOKENS_HERE
#endif

Другой пример,

#ifdef __cplusplus
   #define NAMESPACE_BEGIN(X) namespace X {
   #define NAMESPACE_END }
#else
   #define NAMESPACE_BEGIN(X) 
   #define NAMESPACE_END
#endif
2 голосов
/ 29 сентября 2011

Один странный случай, который я недавно выкопал, чтобы ответить на вопрос, оказался просто комментарием по своей природе.Рассматриваемый код выглядел следующим образом:

void CLASS functionName(){
  //
  //
  //
}

Я обнаружил, что это был просто пустой #define, который автор выбрал для документирования доступа к глобальным переменным в проекте:

Синтаксис C ++: void CLASS functionName ()?

Так что не сильно отличается от того, что сказано /* CLASS */, за исключением исключения опечаток, подобных /* CLAAS */ ... возможно, есть и другие небольшие преимущества(?)

0 голосов
/ 29 сентября 2011

Я согласен с каждым ответом, но я хотел бы указать на небольшую тривиальную вещь.

Будучи пуристом Си, я вырос с утверждением, что КАЖДОЕ И КАЖДОЕ #define должно бытьвыражение, так что, даже если это обычная практика с использованием:

#define WHATEVER

и проверить его с помощью

#ifdef WHATEVER

Я думаю, что всегда лучше писать:

#define WHATEVER (1)

также#debug макросы должны быть выражениями:

#define DEBUG (xxx) (whatever you want for debugging, value)

Таким образом, вы полностью защищены от неправильного использования #macros и предотвращаете неприятные проблемы (особенно в проекте на 10 миллионов строк C)

0 голосов
/ 29 сентября 2011

Существует множество вариантов использования такой вещи.

Например, один макрос должен иметь разное поведение в разных сборках. Например, если вы хотите отладочные сообщения, вы можете получить что-то вроде этого:

#ifdef _DEBUG
  #define DEBUG_LOG(X, ...) however_you_want_to_print_it
#else
  #define DEBUG_LOG(X, ...) // nothing
#endif

Другое использование может быть для настройки вашего заголовочного файла в зависимости от вашей системы. Это из моего реализованного в mesa заголовка OpenGL в linux:

#if !defined(OPENSTEP) && (defined(__WIN32__) && !defined(__CYGWIN__))
#  if defined(__MINGW32__) && defined(GL_NO_STDCALL) || defined(UNDER_CE)  /* The generated DLLs by MingW with STDCALL are not compatible with the ones done by Microsoft's compilers */
#    define GLAPIENTRY 
#  else
#    define GLAPIENTRY __stdcall
#  endif
#elif defined(__CYGWIN__) && defined(USE_OPENGL32) /* use native windows opengl32 */
#  define GLAPIENTRY __stdcall
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
#  define GLAPIENTRY
#endif /* WIN32 && !CYGWIN */

#ifndef GLAPIENTRY
#define GLAPIENTRY
#endif

И используется в объявлениях заголовков, таких как:

GLAPI void GLAPIENTRY glClearIndex( GLfloat c );

GLAPI void GLAPIENTRY glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );

GLAPI void GLAPIENTRY glClear( GLbitfield mask );

...

(я удалил часть для GLAPI)

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

Другие случаи могут быть следующими:

Если макрос не принимает параметры, это может быть просто объявить некоторый случай. Известный пример - защита заголовочных файлов. Другим примером будет что-то вроде этого

#define USING_SOME_LIB

и позже можно использовать так:

#ifdef USING_SOME_LIB
...
#else
...
#endif

Возможно, что макрос использовался на каком-то этапе, чтобы что-то сделать (например, журнал), но затем при выпуске владелец решил, что журнал больше не нужен, и просто удалил содержимое макроса, чтобы он стал пустым. Это не рекомендуется, однако, используйте метод, который я упомянул в самом начале ответа.

Наконец, это может быть просто для дополнительного объяснения, например, вы можете сказать

#define DONT_CALL_IF_LIB_NOT_INITIALIZED

и вы пишете такие функции, как:

void init(void);
void do_something(int x) DONT_CALL_IF_LIB_NOT_INITIALIZED;

Хотя этот последний случай немного абсурден, но в таком случае это имело бы смысл:

#define IN
#define OUT

void function(IN char *a, OUT char *b);
0 голосов
/ 29 сентября 2011

Как вы можете видеть в ответах выше, это может быть полезно при отладке вашего кода.

#ifdef DEBUG
#define debug(msg) fputs(__FILE__ ":" (__LINE__) " - " msg, stderr)
#else
#define debug(msg)
#endif

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

0 голосов
/ 29 сентября 2011
Макросы

#define просто заменяются буквально текстом замены во время предварительной обработки. Если нет заменяющего текста, тогда ... они заменяются ничем! Итак, этот исходный код:

#define FOO(x)

print(FOO(hello world));

будет предварительно обработано как раз так:

print();

Это может быть полезно, чтобы избавиться от вещей, которые вам не нужны, например, скажем, assert(). Это в основном полезно в условных ситуациях, когда в некоторых условиях есть непустое тело.

0 голосов
/ 29 сентября 2011

Это можно использовать, когда вы можете отключить какую-либо функцию. Например, в режиме отладки вы хотите напечатать некоторые операторы отладки, а в рабочем коде вы хотите их опустить:

#ifdef DEBUG
#define PRINT(X) printf("%s", X)
#else
#define PRINT(X)  // <----- silently removed
#endif

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

void foo ()
{
  PRINT("foo() starts\n");
  ...
}
...