Самые полезные сделанные пользователем C-макросы (в GCC, также C99)? [закрытый]

Один веб-сайт, который я использую, имеет метод перевода вызовы владельца "Wiki + машинный перевод". Это - основанный на сообществе сайт, так очевидно отличается от потребностей компаний.

http://blog.bookmooch.com/2007/09/23/how-bookmooch-does-its-translations/

28
задан Brian Tompsett - 汤莱恩 19 February 2016 в 20:28
поделиться

18 ответов

для каждого цикла в 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);
}
14
ответ дан 28 November 2019 в 02:16
поделиться

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

#define ISNAN(x) ((x) != (x))
0
ответ дан 28 November 2019 в 02:16
поделиться

Этот замечательный:

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

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

object = NEW(object_type, 1);
-1
ответ дан 28 November 2019 в 02:16
поделиться

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

0
ответ дан 28 November 2019 в 02:16
поделиться

Упакуйте байты, слова, двойные слова в слова, двойные и двойные слова:

#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) 

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

1
ответ дан 28 November 2019 в 02:16
поделиться

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

-1
ответ дан 28 November 2019 в 02:16
поделиться
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))

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

2
ответ дан 28 November 2019 в 02:16
поделиться

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

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

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

2
ответ дан 28 November 2019 в 02:16
поделиться

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

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

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

2
ответ дан 28 November 2019 в 02:16
поделиться
#define COLUMNS(S,E) [ (E) - (S) + 1 ]


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

Избегайте ошибок при подсчете

5
ответ дан 28 November 2019 в 02:16
поделиться
#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 и т. Д.

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

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

void foo(int array[], int n) {
  assert(IMPLIES(n > 0, array != NULL));
  ...
34
ответ дан 28 November 2019 в 02:16
поделиться

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

  1. Сокращение для фрагментов кода, которые не нужно повторять
  2. Обеспечение функции общего использования
  3. Измените структура языка 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.

22
ответ дан 28 November 2019 в 02:16
поделиться

Это из ядра 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
4
ответ дан 28 November 2019 в 02:16
поделиться

также могут иметь несколько типов Минимум и Максимум, подобные этому

//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; })
0
ответ дан 28 November 2019 в 02:16
поделиться

Кто-то еще упомянул 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 color в приведенном ниже примере быть в более связанных списках, чем только colors ).

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

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);
5
ответ дан 28 November 2019 в 02:16
поделиться

Цикл 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's */
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);
}
6
ответ дан 28 November 2019 в 02:16
поделиться

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

Например, допустим, вы хотите определить перечисление цветов и функцию перечисления в строку, а не перечислять все цвета дважды, вы можете создать файл цветов ( 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]);
};
11
ответ дан 28 November 2019 в 02:16
поделиться
#if defined NDEBUG
    #define TRACE( format, ... )
#else
    #define TRACE( format, ... )   printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__,  __LINE__, __VA_ARGS__ )
#endif

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

7
ответ дан 28 November 2019 в 02:16
поделиться
Другие вопросы по тегам:

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