Редкие случаи, когда необходимо использовать MACRO

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

Каковы редкие случаи?

Ответ 1

Если вы хотите фактическую текстовую замену, то, где вы используете макросы. Взгляните на Boost.Preprocessor, это отличный способ имитировать вариационные шаблоны в С++ 03, не повторяя слишком много.

Другими словами, если вы хотите управлять самим программным кодом, используйте макросы.

Другим полезным приложением является assert, который определяется как no-op, когда NDEBUG не определен (обычно компиляция режима выпуска).

Это приводит нас к следующему пункту, который является специализацией первого: разный код с различными режимами компиляции или между разными компиляторами. Если вы хотите использовать кросс-компилятор, вы не сможете обойтись без макросов. Взгляните на Boost в целом, он все время нуждается в макросах из-за различных недостатков в различных компиляторах, которые он должен поддерживать.

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

#define NEEDS_INFO() \
  has_info(__FILE__, __LINE__, __func__)

С подходящим объявлением has_info (и С++ 11/C99 __func__ или аналогичным).

Ответ 2

У этого вопроса нет определенного ответа с закрытой формой, поэтому я просто приведу несколько примеров.

Предположим, вы хотите распечатать информацию о заданном типе. Имена типов не существуют в скомпилированном коде, поэтому они не могут быть выражены самим языком (кроме расширений С++). Здесь препроцессор должен выполнить следующие действия:

#define PRINT_TYPE_INFO(type) do { printf("sizeof(" #type ") = %zu\n", sizeof(type)); } while (false)

PRINT_TYPE_INFO(int);
PRINT_TYPE_INFO(double);

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

#define DECLARE_SYM(name) fhandle libfoo_##name = dlsym("foo_" #name, lib);

DECLARE_SYM(init);   // looks up "foo_init()", declares "libfoo_init" pointer
DECLARE_SYM(free);
DECLARE_SYM(get);
DECLARE_SYM(set);

Мое любимое использование предназначено для отправки вызовов функций CUDA и проверки их возвращаемого значения:

#define CUDACALL(F, ARGS...) do { e = F(ARGS); if (e != cudaSuccess) throw cudaException(#F, e); } while (false)

CUDACALL(cudaMemcpy, data, dp, s, cudaMemcpyDeviceToHost);
CUDACALL(cudaFree, dp);

Ответ 3

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

Если вы хотите написать функцию-обертку над свободной функцией, например say malloc, не изменяя каждый экземпляр вашего кода, где вызывается функция, достаточно простого макроса:

#define malloc(X) my_malloc( X, __FILE__, __LINE__, __FUNCTION__)

void* my_malloc(size_t size, const char *file, int line, const char *func)
{

    void *p = malloc(size);
    printf ("Allocated = %s, %i, %s, %p[%li]\n", file, line, func, p, size);

    /*Link List functionality goes in here*/

    return p;
}

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

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

Ответ 4

Одним из примеров является прикрепление токенов, если вы хотите использовать значение как идентификатор, так и значение. Из ссылки msdn:

#define paster( n ) printf_s( "token" #n " = %d", token##n )
int token9 = 9;

paster( 9 ); // => printf_s( "token9 = %d", token9 );

Есть также случаи в С++ faq, хотя, возможно, есть альтернативы, решение макросов - лучший способ сделать что-то. Одним из примеров является указатели на функции-члены, где правый макрос

 #define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember)) 

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

int ans = CALL_MEMBER_FN(fred,p)('x', 3.14);

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

Вот пример кто-то пытается сделать это самостоятельно

Ответ 5

Если вам нужен сам вызов для необязательного возврата из функции.

#define MYMACRO(x) if(x) { return; }
void fn()
{
    MYMACRO(a);
    MYMACRO(b);
    MYMACRO(c);
}

Обычно это используется для небольших битов повторяющегося кода.

Ответ 6

Я не уверен, что отладка макросов занимает много времени. Я считаю, что я нахожу простую отладку макросов (даже 100 строк монстров-макросов), потому что у вас есть возможность взглянуть на расширение (например, с помощью gcc -C -E), что менее возможно с помощью, например, Шаблоны С++.

Макросы

C полезны, когда несколько раз:

  • вы хотите обработать список вещей несколькими способами.
  • вы хотите определить выражение "lvalue"
  • вам нужна эффективность.
  • вам нужно указать местоположение макроса через __LINE__)
  • вам нужны уникальные идентификаторы
  • и т.д.

Посмотрите на многочисленные применения макросов #define -d внутри основного свободного программного обеспечения (например, Gtk, Gcc, Qt,...)

Я очень сожалею о том, что язык макросов C настолько ограничен... Представьте, что макрос C был бы столь же мощным, как Guile!!! (Тогда вы могли бы написать такие сложные вещи, как flex или bison в качестве макросов).

Посмотрите на мощь общих макросов Lisp!

Ответ 7

Если вы используете C, вам нужно использовать макросы для имитации шаблонов.

Из http://www.flipcode.com/archives/Faking_Templates_In_C.shtml

#define CREATE_VECTOR_TYPE_H(type) \
typedef struct _##type##_Vector{ \
  type *pArray; \
  type illegal; \
  int size; \
  int len; \
} type##_Vector; \
void type##_InitVector(type##_Vector *pV, type illegal); \
void type##_InitVectorEx(type##_Vector *pV, int size, type illegal); \
void type##_ClearVector(type##_Vector *pV); \
void type##_DeleteAll(type##_Vector *pV); \
void type##_EraseVector(type##_Vector *pV); \
int type##_AddElem(type##_Vector *pV, type Data); \
type type##_SetElemAt(type##_Vector *pV, int pos, type data); \
type type##_GetElemAt(type##_Vector *pV, int pos);

#define CREATE_VECTOR_TYPE_C(type) \
void type##_InitVector(type##_Vector *pV, type illegal) \
{ \
  type##_InitVectorEx(pV, DEF_SIZE, illegal); \
} \
void type##_InitVectorEx(type##_Vector *pV, int size, type illegal) \
{ \
  pV-len = 0; \
  pV-illegal = illegal; \
  pV-pArray = malloc(sizeof(type) * size); \
  pV-size = size; \
} \
void type##_ClearVector(type##_Vector *pV) \
{ \
  memset(pV-pArray, 0, sizeof(type) * pV-size); \
  pV-len = 0; \
} \
void type##_EraseVector(type##_Vector *pV) \
{ \
  if(pV-pArray != NULL) \
    free(pV-pArray); \
  pV-len = 0; \
  pV-size = 0; \
  pV-pArray = NULL; \
} \
int type##_AddElem(type##_Vector *pV, type Data) \
{ \
  type *pTmp; \
  if(pV-len = pV-size) \
  { \
    pTmp = malloc(sizeof(type) * pV-size * 2); \
    if(pTmp == NULL) \
      return -1; \
    memcpy(pTmp, pV-pArray, sizeof(type) * pV-size); \
    free(pV-pArray); \
    pV-pArray = pTmp; \
    pV-size *= 2; \
  } \
  pV-pArray[pV-len] = Data; \
  return pV-len++; \
} \
type type##_SetElemAt(type##_Vector *pV, int pos, type data) \
{ \
  type old = pV-illegal; \
  if(pos = 0 && pos <= pV-len) \
  { \
    old = pV-pArray[pos]; \
    pV-pArray[pos] = data; \
  } \
  return old; \
} \
type type##_GetElemAt(type##_Vector *pV, int pos) \
{ \
  if(pos = 0 && pos <= pV-len) \
    return pV-pArray[pos]; \
  return pV-illegal; \
} 

Ответ 8

Рассмотрим стандартный макрос assert.

  • Он использует условную компиляцию, чтобы гарантировать, что код включен только в отладочные сборки (вместо того, чтобы полагаться на оптимизатор, чтобы его реализовать).
  • Он использует макросы __FILE__ и __LINE__ для создания ссылок на местоположение в исходном коде.

Ответ 9

Я когда-то использовал макрос для создания большого массива строк вместе с перечислением индекса:

strings.inc

GEN_ARRAY(a)
GEN_ARRAY(aa)
GEN_ARRAY(abc)
GEN_ARRAY(abcd)
// ...

strings.h

// the actual strings
#define GEN_ARRAY(x) #x ,
const char *strings[]={
    #include "strings.inc"
    ""
};
#undef GEN_ARRAY

// indexes
#define GEN_ARRAY(x) enm_##x ,
enum ENM_string_Index{
    #include "strings.inc"
    enm_TOTAL
};
#undef GEN_ARRAY

Это полезно, когда у вас есть несколько массивов, которые необходимо синхронизировать.

Ответ 10

Чтобы расширить на @tenfour ответ об условных возвратах: я делаю это много при написании кода Win32/COM, где кажется, что я проверяю HRESULT каждую вторую строку. Например, сравните раздражающий способ:

// Annoying way:
HRESULT foo() {
    HRESULT hr = SomeCOMCall();
    if (SUCCEEDED(hr)) {
        hr = SomeOtherCOMCall();
    }

    if (SUCCEEDED(hr)) {
        hr = SomeOtherCOMCall2();
    }

    // ... ad nauseam.

    return hr;
}

С макро-y приятным способом:

// Nice way:
HRESULT foo() {
    SUCCEED_OR_RETURN(SomeCOMCall());
    SUCCEED_OR_RETURN(SomeOtherCOMCall());
    SUCCEED_OR_RETURN(SomeOtherCOMCall2());

    // ... ad nauseam.

    // If control makes it here, nothing failed.
    return S_OK;
}

Это вдвойне удобно, если вы подключаете макрос, чтобы автоматически регистрировать любые сбои: используя другие идеи макросов, такие как вставку токенов и FILE, LINE и т.д.; Я даже могу сделать запись в журнале содержащей место кода и выражение, которое не удалось. Вы также можете бросить там утверждение, если хотите!

#define SUCCEED_OR_RETURN(expression) { \
    HRESULT hrTest = (expression); \
    if (!SUCCEEDED(hrTest)) { \
        logFailure( \
            #expression, \
            HResultValueToString(hrTest), \
            __FILE__, \
            __LINE__, \
            __FUNCTION__); \
        return hrTest; \
    } \
}

Ответ 11

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

Ответ 12

Для меня удобнее использовать макросы для констант и для частей кода, которые не имеют разделенных логических функций. Но есть некоторые важные различия между (встроенными) функциями и (функциональными) макросами, вот они: http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx