Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 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