2013-04-24 3 views
5

Я искал макрос, который будет похож на конструкцию. Использование должно быть что-то вроде:"with" macro in C

with (lock(&x), unlock(&x)) { 
    ... 
} 

Это может быть полезно для некоторых других целей.

Я придумал этот макрос:

#define __with(_onenter, _onexit, v) \ 
    for (int __with_uniq##v=1; __with_uniq##v > 0;)\ 
     for (_onenter; __with_uniq##v > 0; _onexit) \ 
      while (__with_uniq##v-- > 0) 

#define _with(x, y, z) __with(x, y, z) 
#define with(_onenter, _onexit) _with(_onenter, _onexit, __COUNTER__) 

Он имеет 3 вложенных циклов, так как он должен:

  1. Инициализировать счетчик цикла (только C99, конечно)
  2. Возможно инициализировать переменную _onenter (например, with (int fd=open(..), close(fd)))
  3. Разрешить break внутри кодового блока. (continue допускается тоже. А макрос может быть скорректирована, чтобы assert() его)

Я использовал его на код для xv6 OS и кажется весьма полезным.

Мой вопрос - какие самые худшие проблемы с таким макросом? Я имею в виду, кроме простого использования макроса C (особенно тот, который реализует новую конструкцию потока управления).

До сих пор нашел эти недостатки/проблемы:

  1. Нет поддержки return или goto (но это может сэкономить goto с в коде ядра)
  2. Нет поддержки ошибок (например, fd < 0). Я думаю, что это исправление.
  3. gnu89/c99 и выше только (счетчик циклов. Уникальный трюк не нужен)
  4. Несколько менее эффективный, чем простой замок-разблокировка. Я считаю, что это несущественно.

Есть ли другие проблемы? Есть ли лучший способ реализовать аналогичную конструкцию в C?

ответ

6

Этот макрос меня пугает. Я бы предпочел traditional approach using gotos.

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

Ваш макрос умный, но он будет новым для большинства людей, и он поставляется со скрытыми gotchas. Новые участники должны были бы считаться такими правилами, как «не делать return или goto из блока с блоком» и «break выйдет из блока с, а не из окружающего цикла». Я боюсь, что ошибки будут распространены.

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

Если вы хотите ограничить себя GCC и Clang, вы можете использовать атрибут cleanup.Это сделает ваш пример выглядеть следующим образом:

lock_t x = NULL __attribute__((cleanup(unlock))); 
lock(&x); 

И unlock будет вызвана с указателем на переменную, когда она выходит из области видимости. Это интегрируется с другими языковыми функциями, такими как return и goto, и даже с исключениями в смешанных проектах C/C++.

+0

Ох. ответ наконец ... Спасибо, я не знал о 'cleanup' - звучит полезно. Есть ли у макроса более специфические проблемы, помимо его скукости? – Elazar

+1

Отсутствие поддержки для 'return' кажется сделкой, если вы не работаете со строгим стандартом кодирования в отношении операторов' return' в середине функций. «Возврат» в блоке 'with' будет выглядеть незаметно, но наносит вред во время выполнения. – pdw

+0

Я понимаю и соглашусь с тем, что 'return' - большая проблема (так что' cleanup' намного лучше). Но разве 'goto' не требует строгого стандарта кодирования? Каковы преимущества goto, в этом отношении? – Elazar