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

Наиболее полезные пользовательские C-макросы (в GCC, также C99)?

Какой макрос C является, по вашему мнению, наиболее полезным? Я нашел следующий, который я использую для векторной арифметики в C:

#define v3_op_v3(x, op, y, z) {z[0]=x[0] op y[0]; \
                               z[1]=x[1] op y[1]; \
                               z[2]=x[2] op y[2];}

Он работает следующим образом:

v3_op_v3(vectorA, +, vectorB, vectorC);
v3_op_v3(vectorE, *, vectorF, vectorJ);
...
4b9b3361

Ответ 1

для каждого цикла в C99:

#define foreach(item, array) \
    for(int keep=1, \
            count=0,\
            size=sizeof (array)/sizeof *(array); \
        keep && count != size; \
        keep = !keep, count++) \
      for(item = (array)+count; keep; keep = !keep)

int main() {
  int a[] = { 1, 2, 3 };
  int sum = 0;
  foreach(int const* c, a)
    sum += *c;
  printf("sum = %d\n", sum);

  // multi-dim array
  int a1[][2] = { { 1, 2 }, { 3, 4 } };
  foreach(int (*c1)[2], a1)
    foreach(int *c2, *c1) 
      printf("c2 = %d\n", *c2);
}

Ответ 2

#define IMPLIES(x, y) (!(x) || (y))

#define COMPARE(x, y) (((x) > (y)) - ((x) < (y)))
#define SIGN(x) COMPARE(x, 0)

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))

#define SWAP(x, y, T) do { T tmp = (x); (x) = (y); (y) = tmp; } while(0)
#define SORT2(a, b, T) do { if ((a) > (b)) SWAP((a), (b), T); } while (0)

#define SET(d, n, v) do{ size_t i_, n_; for (n_ = (n), i_ = 0; n_ > 0; --n_, ++i_) (d)[i_] = (v); } while(0)
#define ZERO(d, n) SET(d, n, 0)

И, конечно, различные MIN, MAX, ABS и т.д.

Обратите внимание, BTW, что ни одно из перечисленных выше не может быть реализовано функцией в C.

P.S. Я бы выделил выше макрос IMPLIES как один из самых полезных. Его основная цель - облегчить написание более элегантных и читаемых утверждений, как в

void foo(int array[], int n) {
  assert(IMPLIES(n > 0, array != NULL));
  ...

Ответ 3

Ключевым моментом с макросами C является их правильное использование. На мой взгляд, есть три категории (не рассматривая их использование, чтобы дать описательные имена константам)

  • В качестве сокращения для кусков кодов нельзя повторять
  • Предоставить общую функцию использования
  • Измените структуру языка C (видимо)

В первом случае ваш макрос будет жить только внутри вашей программы (обычно это просто файл), поэтому вы можете использовать макросы, подобные тому, который вы опубликовали, который не защищен от двойной оценки параметров и использует {...}; (потенциально опасный!).

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

Макрос, который вы опубликовали из GCC (min и max), является примером этого, они используют глобальные переменные _a и _b, чтобы избежать риска двойной оценки (например, в max(x++,y++)) (ну, они используйте расширения GCC, но концепция такая же).

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

Я вижу, что другие приводили примеры пункта 2 (макросы как функции), позвольте мне привести пример создания новой конструкции C: конечный конечный автомат. (Я уже разместил это на SO, но, похоже, я не могу его найти)

 #define FSM            for(;;)
 #define STATE(x)       x##_s 
 #define NEXTSTATE(x)   goto x##_s

что вы используете этот способ:

 FSM {
    STATE(s1):
      ... do stuff ...
      NEXTSTATE(s2);

    STATE(s2):
      ... do stuff ...
      if (k<0) NEXTSTATE(s2); 
      /* fallthrough as the switch() cases */

    STATE(s3):
      ... final stuff ...
      break;  /* Exit from the FSM */
 } 

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

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

Ответ 4

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

Например, скажем, вы хотите определить перечисление цветов и функцию перечисления в строку, а затем перечислить все цвета дважды, вы можете создать файл с цветами (colors.def):

c(red)
c(blue)
c(green)
c(yellow)
c(brown)

Теперь вы можете в своем c файле определить свою перечисление и функцию преобразования строк:

enum {
#define c(color) color,
# include "colors.def"
#undef c
};

const char *
color_to_string(enum color col)
{
    static const char *colors[] = {
#define c(color) #color,
# include "colors.def"
#undef c
    };
    return (colors[col]);
};

Ответ 5

#if defined NDEBUG
    #define TRACE( format, ... )
#else
    #define TRACE( format, ... )   printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__,  __LINE__, __VA_ARGS__ )
#endif

Обратите внимание, что отсутствие запятой между "%s::%s(%d)" и format является преднамеренным. Он печатает форматированную строку с исходным расположением. Я работаю в встроенных системах реального времени, поэтому часто включаю также метку времени на выходе.

Ответ 6

Цикл Foreach для GCC, в частности C99 с расширениями GNU. Работает со строками и массивами. Динамически распределенные массивы можно использовать, переведя их в указатель на массив, а затем разыгрывая их.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define FOREACH_COMP(INDEX, ARRAY, ARRAY_TYPE, SIZE) \
  __extension__ \
  ({ \
    bool ret = 0; \
    if (__builtin_types_compatible_p (const char*, ARRAY_TYPE)) \
      ret = INDEX < strlen ((const char*)ARRAY); \
    else \
      ret = INDEX < SIZE; \
    ret; \
  })

#define FOREACH_ELEM(INDEX, ARRAY, TYPE) \
  __extension__ \
  ({ \
    TYPE *tmp_array_ = ARRAY; \
    &tmp_array_[INDEX]; \
  })

#define FOREACH(VAR, ARRAY) \
for (void *array_ = (void*)(ARRAY); array_; array_ = 0) \
for (size_t i_ = 0; i_ && array_ && FOREACH_COMP (i_, array_, \
                                    __typeof__ (ARRAY), \
                                    sizeof (ARRAY) / sizeof ((ARRAY)[0])); \
                                    i_++) \
for (bool b_ = 1; b_; (b_) ? array_ = 0 : 0, b_ = 0) \
for (VAR = FOREACH_ELEM (i_, array_, __typeof__ ((ARRAY)[0])); b_; b_ = 0)

/* example */
int
main (int argc, char **argv)
{
  int array[10];
  /* initialize the array */
  int i = 0;
  FOREACH (int *x, array)
    {
      *x = i;
      ++i;
    }

  char *str = "hello, world!";
  FOREACH (char *c, str)
    printf ("%c\n", *c);

  /* Use a cast for dynamically allocated arrays */
  int *dynamic = malloc (sizeof (int) * 10);
  for (int i = 0; i < 10; i++)
    dynamic[i] = i;

  FOREACH (int *i, *(int(*)[10])(dynamic))
    printf ("%d\n", *i);

  return EXIT_SUCCESS;
}

Этот код был протестирован для работы с GCC, ICC и Clang на GNU/Linux.

Лямбда-выражения (только GCC)

#define lambda(return_type, ...) \
  __extension__ \
  ({ \
    return_type __fn__ __VA_ARGS__ \
    __fn__; \
  })

int
main (int argc, char **argv)
{
  int (*max) (int, int) = 
    lambda (int, (int x, int y) { return x > y ? x : y; });
  return max (1, 2);
}

Ответ 7

#define COLUMNS(S,E) [ (E) - (S) + 1 ]


struct 
{
    char firstName COLUMNS ( 1, 20);
    char LastName  COLUMNS (21, 40);
    char ssn       COLUMNS (41, 49);
}

Сэкономьте себе подсчет с ошибкой

Ответ 8

Кто-то еще упомянул container_of(), но не дал объяснений этому действительно удобному макросу. Скажем, у вас есть структура, которая выглядит так:

struct thing {
    int a;
    int b;
};

Теперь, если у нас есть указатель на b, мы можем использовать container_of(), чтобы получить указатель на вещь в безопасном типе

int *bp = ...;
struct thing *t = container_of(bp, struct thing, b);

Это полезно при создании абстрактных структур данных. Например, вместо принятия подхода queue.h требуется для создания таких вещей, как SLIST (тонны сумасшедших макросов для каждой операции), теперь вы можете написать реализацию slist, которая выглядит примерно так:

struct slist_el {
    struct slist_el *next;
};

struct slist_head {
    struct slist_el *first;
};

void
slist_insert_head(struct slist_head *head, struct slist_el *el)
{
    el->next = head->first;
    head->first = el;
}

struct slist_el
slist_pop_head(struct slist_head *head)
{
    struct slist_el *el;

    if (head->first == NULL)
        return NULL;

    el = head->first;
    head->first = el->next;
    return (el);   
}

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

Теперь пользователи могут использовать вашу библиотеку следующим образом:

struct colors {
    int r;
    int g;
    int b;
    struct slist_el colors;
};

struct *color = malloc(sizeof(struct person));
color->r = 255;
color->g = 0;
color->b = 0;
slist_insert_head(color_stack, &color->colors);
...
el = slist_pop_head(color_stack);
color = el == NULL ? NULL : container_of(el, struct color, colors);

Ответ 9

Это одно из ядра linux (спецификация gcc):

#define container_of(ptr, type, member) ({                  \
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) ); })

Еще один недостаток в других ответах:

#define LSB(x) ((x) ^ ((x) - 1) & (x))   // least significant bit

Ответ 10

Мне тоже нравится:

#define COMPARE_FLOATS(a,b,epsilon) (fabs(a - b) <= epsilon * fabs(a))

И как вы, матросы-ненавистники, проводите честные сравнения с плавающей запятой?

Ответ 11

Только стандартные:

#define LENGTH(array) (sizeof(array) / sizeof (array[0]))
#define QUOTE(name) #name
#define STR(name) QUOTE(name)

но там ничего слишком крутого.

Ответ 12

#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))

Найдите ближайшее 32-битное целое число без знака, большее, чем x. Я использую это, чтобы удвоить размер массивов (т.е. Знак высокой воды).

Ответ 13

Пакет байтов, слов, слов в слова, слова и слова:

#define ULONGLONG unsigned __int64
#define MAKEWORD(h,l) ((unsigned short) ((h) << 8)) | (l)
#define MAKEDWORD(h,l) ((DWORD) ((h) << 16)) | (l)
#define MAKEQWORD(h,l) ((ULONGLONG)((h) << 32)) | (l) 

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

Ответ 14

также многотипные минимальные и максимальные значения, такие как

//NOTE: GCC extension !
#define max(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a > _b ? _a:_b; })
#define min(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a < _b ? _a:_b; })

Ответ 15

Проверка, является ли плавающая точка x не числом:

#define ISNAN(x) ((x) != (x))

Ответ 16

Один (из немногих), который я использую регулярно, является макросом, чтобы объявить аргумент или переменную как неиспользованные. Наиболее совместимое решение, чтобы отметить это (IMHO), зависит от компилятора.

Ответ 17

Это потрясающе:

#define NEW(type, n) ( (type *) malloc(1 + (n) * sizeof(type)) )

И я использую его как:

object = NEW(object_type, 1);

Ответ 18

ИСТИНА и ЛОЖЬ кажутся популярными.