Каковы приложения ## оператора препроцессора и глюков для рассмотрения?

86
задан Lundin 9 February 2016 в 10:30
поделиться

12 ответов

CrashRpt: Используя ## для преобразования макро-байтовых строк в Unicode

интересное использование в CrashRpt (библиотека создания отчетов катастрофического отказа) следующее:

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
//Note you need a WIDEN2 so that __DATE__ will evaluate first.

Здесь они хотят использовать две строки байтов вместо строки one-byte-per-char. Это, вероятно, похоже на него, действительно бессмысленно, но они делают это на серьезном основании.

 std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

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

Помещение L рядом с __ DATE __ дало бы Вам ошибку компиляции.

<час>

Windows: Используя ## для универсального Unicode или байтовых строк

Windows использует что-то как следующее:

#ifdef  _UNICODE
    #define _T(x)      L ## x
#else
    #define _T(x) x
#endif

И _T используется везде в коде

<час>

библиотеки Various, использующие для чистого средства доступа и имен модификатора:

я также видел, что это раньше в коде определяло средства доступа и модификаторы:

#define MYLIB_ACCESSOR(name) (Get##name)
#define MYLIB_MODIFIER(name) (Set##name)

Аналогично можно использовать этот тот же метод для любых других типов умного создания имени.

<час>

библиотеки Various, с помощью него для создания нескольких объявлений переменной сразу:

#define CREATE_3_VARS(name) name##1, name##2, name##3
int CREATE_3_VARS(myInts);
myInts1 = 13;
myInts2 = 19;
myInts3 = 77;
46
ответ дан Peter Mortensen 24 November 2019 в 08:05
поделиться

Это очень полезно для входа. Можно сделать:

#define LOG(msg) log_msg(__function__, ## msg)

Или, если Ваш компилятор не поддерживает функция и func:

#define LOG(msg) log_msg(__file__, __line__, ## msg)

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

Мой синтаксис C++ мог бы быть не совсем корректным.

0
ответ дан ya23 24 November 2019 в 08:05
поделиться

Основное использование состоит в том, когда у Вас есть соглашение о присвоении имен, и Вы хотите, чтобы Ваш макрос использовал в своих интересах то соглашение о присвоении имен. Возможно, у Вас есть несколько семейств методов: image_create (), image_activate (), и image_release () также file_create (), file_activate (), file_release (), и mobile_create (), mobile_activate () и mobile_release ().

Вы могли записать макрос для обработки объектного жизненного цикла:

#define LIFECYCLE(name, func) (struct name x = name##_create(); name##_activate(x); func(x); name##_release())

, Конечно, своего рода "минимальная версия объектов" не является единственным видом соглашения о присвоении имен, к которому это относится - почти подавляющее большинство соглашений о присвоении имен использует общую подстроку для формирования имен. Это могло меня имена функций (как выше), или имена полей, имена переменной или большинство что-либо еще.

1
ответ дан mcherm 24 November 2019 в 08:05
поделиться

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

UNITTEST(test_name)

расширяется до:

void __testframework_test_name ()
1
ответ дан John Millikin 24 November 2019 в 08:05
поделиться

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



#define ASSERT(exp) if(!(exp)){ \
                      print_to_rs232("Assert failed: " ## #exp );\
                      while(1){} //Let the watchdog kill us 


2
ответ дан c0m4 24 November 2019 в 08:05
поделиться

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

Это может использоваться для шаблонов:

#define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};

В этом случае LINKED_LIST (интервал) дал бы Вам

struct list_int {
int value;
struct list_int *next;
};

Так же, можно записать шаблон функции для обхода списка.

3
ответ дан Peter Mortensen 24 November 2019 в 08:05
поделиться

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

SCREEN_HANDLER( activeCall )

расширяется до чего-то вроде этого:

STATUS activeCall_constructor( HANDLE *pInst )
STATUS activeCall_eventHandler( HANDLE *pInst, TOKEN *pEvent );
STATUS activeCall_destructor( HANDLE *pInst );

Это осуществляет корректную параметризацию для всех "полученных" объектов, когда Вы делаете:

SCREEN_HANDLER( activeCall )
SCREEN_HANDLER( ringingCall )
SCREEN_HANDLER( heldCall )

вышеупомянутое в Ваших заголовочных файлах, и т.д. Для обслуживания также полезно, если Вы даже, оказывается, хотите изменить определения и/или добавить методы к "объектам".

2
ответ дан Tall Jeff 24 November 2019 в 08:05
поделиться

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

Ссылка

Мой ответ на тот вопрос показал, как применение небольшого волшебства препроцессора позволяет Вам определить свое перечисление как это (например)...;

ENUM_BEGIN( Color )
  ENUM(RED),
  ENUM(GREEN),
  ENUM(BLUE)
ENUM_END( Color )

... С преимуществом, что макрорасширение не только определяет перечисление (в.h файле), это также определяет массив строк соответствия (в.c файле);

const char *ColorStringTable[] =
{
  "RED",
  "GREEN",
  "BLUE"
};

название таблицы строк происходит от вставки макро-параметра (т.е. Цвет) в StringTable с помощью ## оператора. Приложения (приемы?) как это то, где # и ## операторы неоценимы.

4
ответ дан Community 24 November 2019 в 08:05
поделиться

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

Ненужное использование вставляющего маркер оператора (##) является непортативным и может генерировать нежелательный пробел, предупреждения или ошибки.

, Когда результатом вставляющего маркер оператора не является допустимый маркер препроцессора, вставляющий маркер оператор является ненужным и возможно вредным.

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

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a##+##b)
#define NS(a, b) STRINGIFY(a##::##b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

На некоторых компиляторах, это произведет ожидаемый результат:

1+2 std::vector

На других компиляторах, это будет включать нежелательный пробел:

1 + 2 std :: vector

Довольно современные версии GCC (> приблизительно =3.3) не скомпилируют этот код:

foo.cpp:16:1: pasting "1" and "+" does not give a valid preprocessing token
foo.cpp:16:1: pasting "+" and "2" does not give a valid preprocessing token
foo.cpp:16:1: pasting "std" and "::" does not give a valid preprocessing token
foo.cpp:16:1: pasting "::" and "vector" does not give a valid preprocessing token

решение состоит в том, чтобы опустить вставляющий маркер оператор при конкатенации маркеров препроцессора к операторам C/C++:

#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a+b)
#define NS(a, b) STRINGIFY(a::b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));

глава документации GCC CPP по конкатенации имеет более полезную информацию о вставляющем маркер операторе.

14
ответ дан bk1e 24 November 2019 в 08:05
поделиться

Это полезно во всех видах ситуаций для не повторения себя напрасно. Следующее является примером от исходного кода Emacs. Мы хотели бы загрузить много функций из библиотеки. Функциональное "нечто" должно быть присвоено fn_foo и так далее. Мы определяем следующий макрос:

#define LOAD_IMGLIB_FN(lib,func) {                                      \
    fn_##func = (void *) GetProcAddress (lib, #func);                   \
    if (!fn_##func) return 0;                                           \
  }

Мы можем тогда использовать его:

LOAD_IMGLIB_FN (library, XpmFreeAttributes);
LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
LOAD_IMGLIB_FN (library, XpmReadFileToImage);
LOAD_IMGLIB_FN (library, XImageFree);

преимущество не должно писать и fn_XpmFreeAttributes и "XpmFreeAttributes" (и рисковать писать одного c орфографическими ошибками из них).

6
ответ дан Vebjorn Ljosa 24 November 2019 в 08:05
поделиться

Одна вещь знать при использовании маркерной вставки (' ##') или stringizing (' #') предварительная обработка операторов состоит в том, что необходимо использовать дополнительный уровень абстракции для них для работы правильно во всех случаях.

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

#include <stdio.h>

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main() 
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}

вывод:

buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21
49
ответ дан Michael Burr 24 November 2019 в 08:05
поделиться

SGlib использует ##, чтобы в основном уклониться от шаблонов в C. Поскольку нет никакой перегрузки функции, ## используется для склеивания имени типа на названия сгенерированных функций. Если бы у меня был тип списка, названный list_t, то я получил бы функции, названные как sglib_list_t_concat и так далее.

2
ответ дан 24 November 2019 в 08:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: