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;
Это очень полезно для входа. Можно сделать:
#define LOG(msg) log_msg(__function__, ## msg)
Или, если Ваш компилятор не поддерживает функция и func:
#define LOG(msg) log_msg(__file__, __line__, ## msg)
вышеупомянутое сообщение журналов "функций" и шоу точно, которые функционируют, зарегистрировали сообщение.
Мой синтаксис C++ мог бы быть не совсем корректным.
Основное использование состоит в том, когда у Вас есть соглашение о присвоении имен, и Вы хотите, чтобы Ваш макрос использовал в своих интересах то соглашение о присвоении имен. Возможно, у Вас есть несколько семейств методов: 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())
, Конечно, своего рода "минимальная версия объектов" не является единственным видом соглашения о присвоении имен, к которому это относится - почти подавляющее большинство соглашений о присвоении имен использует общую подстроку для формирования имен. Это могло меня имена функций (как выше), или имена полей, имена переменной или большинство что-либо еще.
Я использую его для добавления пользовательских префиксов к переменным, определенным макросами. Так что-то как:
UNITTEST(test_name)
расширяется до:
void __testframework_test_name ()
Я использую его для прокрученного дома, утверждают на нестандартном компиляторе C для встроенного:
#define ASSERT(exp) if(!(exp)){ \
print_to_rs232("Assert failed: " ## #exp );\
while(1){} //Let the watchdog kill us
Можно использовать маркерную вставку, когда необходимо связать макро-параметры с чем-то еще.
Это может использоваться для шаблонов:
#define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};
В этом случае LINKED_LIST (интервал) дал бы Вам
struct list_int {
int value;
struct list_int *next;
};
Так же, можно записать шаблон функции для обхода списка.
Я использую его в программах 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 )
вышеупомянутое в Ваших заголовочных файлах, и т.д. Для обслуживания также полезно, если Вы даже, оказывается, хотите изменить определения и/или добавить методы к "объектам".
Предыдущий вопрос на Stack Переполнение, которое попросили гладкого метода генерации строковых представлений для констант перечисления без большого подверженного ошибкам перепечатывания.
Мой ответ на тот вопрос показал, как применение небольшого волшебства препроцессора позволяет Вам определить свое перечисление как это (например)...;
ENUM_BEGIN( Color )
ENUM(RED),
ENUM(GREEN),
ENUM(BLUE)
ENUM_END( Color )
... С преимуществом, что макрорасширение не только определяет перечисление (в.h файле), это также определяет массив строк соответствия (в.c файле);
const char *ColorStringTable[] =
{
"RED",
"GREEN",
"BLUE"
};
название таблицы строк происходит от вставки макро-параметра (т.е. Цвет) в StringTable с помощью ## оператора. Приложения (приемы?) как это то, где # и ## операторы неоценимы.
Вот глюк, с которым я столкнулся при обновлении до новой версии компилятора:
Ненужное использование вставляющего маркер оператора (##
) является непортативным и может генерировать нежелательный пробел, предупреждения или ошибки.
, Когда результатом вставляющего маркер оператора не является допустимый маркер препроцессора, вставляющий маркер оператор является ненужным и возможно вредным.
, Например, можно было бы попытаться создать строковые литералы во время компиляции с помощью вставляющего маркер оператора:
#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 по конкатенации имеет более полезную информацию о вставляющем маркер операторе.
Это полезно во всех видах ситуаций для не повторения себя напрасно. Следующее является примером от исходного кода 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 орфографическими ошибками из них).
Одна вещь знать при использовании маркерной вставки (' ##
') или 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
SGlib использует ##, чтобы в основном уклониться от шаблонов в C. Поскольку нет никакой перегрузки функции, ## используется для склеивания имени типа на названия сгенерированных функций. Если бы у меня был тип списка, названный list_t, то я получил бы функции, названные как sglib_list_t_concat и так далее.