#c #preprocessor #compile-time
Вопрос:
Предположим, я хочу убедиться, что функция всегда вызывается с другим значением параметра, во время компиляции.
Идеально было бы, чтобы это компилировалось:
enum en {
en_A,
en_B
};
...
foo(en_A);
Но это не удается:
enum en {
en_A,
en_B
};
...
foo(en_A);
...
foo(en_A);
Итак, мы должны иметь возможность вызывать некоторую функцию foo(en_A) только один раз (то же самое для foo(en_B)).
Менее предпочтительным, но также приемлемым было бы определить foo_en_A, foo_en_B и убедиться, что каждый из них вызывается только один раз.
Возможно ли сделать что-то подобное во время компиляции на C?
Комментарии:
1. Почему именно вы хотите это сделать? Из-за существования таких вещей, как циклы и функции-оболочки, кажется, что это не даст никаких значимых гарантий.
2. Я не могу представить какой-либо способ сделать это во время компиляции.
3. Я не вижу никакого механизма в C, который позволил бы такое. Вы можете просто скомпилировать разные файлы C и связать их вместе. Как компилятор сможет обнаруживать вызовы в других файлах C? Если вам действительно нужно такое ограничение, вам нужно будет обработать его во время выполнения.
4. Что, если кто-то записывает
void wrap_foo_en_A(void) { foo(en_A); }
, а затем выполняетwrap_foo_en_A();
в двух разных местах?5. Типичный способ передачи идентифицирующей информации в процедуру ведения журнала — это написать макрос, который передает
__FILE__
и / или__func__
и__LINE__
в процедуру ведения журнала.
Ответ №1:
Возможно ли сделать что-то подобное во время компиляции на C?
Нет, это невозможно. Не только языку программирования C не хватает отражения — он не может проверять себя, но и язык программирования C компилирует по одной «единице перевода» за раз. Хотя с помощью расширений компилятора частично возможно реализовать эту проверку в одном TU, вам придется предоставить специальный компоновщик или плагин компоновщика для реализации этого в нескольких TU.
Чтобы делать то, что вы хотите во время компиляции, вы должны использовать внешние инструменты, помимо самого исходного кода C. Эти инструменты могут работать с сгенерированной программой, проверяя сборку, или могут работать с самим исходным кодом C.
(Вы задаете вопрос XY).
убедитесь, что пользователи библиотеки ведения журнала никогда не делают вызов неоднозначным (например, устанавливают одну и ту же ошибку из двух разных мест), чтобы это место в коде, которое вызвало ошибку, можно было проследить неоднозначно
Для этого просто войдите __FILE__
__LINE__
в систему и наймите нормальных программистов, которые никогда не помещали бы неуникальные сообщения журнала в одну строку или компилировали разные файлы с одинаковым путем.
В любом случае, есть и другой способ решения проблемы. Вместо того, чтобы требовать от программиста вводить уникальный номер по всей базе кода, просто сгенерируйте их числа. Однажды я работал в небольшой встраиваемой системе с очень низкими коммуникационными возможностями, порядка байтов в час. Инструмент, написанный в оболочке с awk
, будет сканировать всю базу кода на наличие именно этой строки UNIQUE()
, и каждый такой вызов будет заменен уникальным номером по всей базе исходного кода, а затем скомпилирован. Таким образом, вместо того, чтобы «требовать от программистов наличия уникальных номеров», числа генерируются сами по себе, что намного проще запрограммировать, чем проверять, был ли такой-то номер уже использован всеми вашими коллегами.
Комментарии:
1. Стоит 2, если бы это было возможно.
Ответ №2:
Я подозреваю, что предварительная обработка в целом затруднена по тем же причинам, что и проблема остановки. Практически, если вы должны были запустить эту систему без NDEBUG
этого, это приведет assert
к тому, что функция не будет вызываться с одним и тем же вводом дважды.
#include <stdio.h>
#include <limits.h>
#include <assert.h>
/* http://c-faq.com/misc/bitsets.html */
#define BITMASK(b) (1 << ((b) % CHAR_BIT))
#define BITSLOT(b) ((b) / CHAR_BIT)
#define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
#define BITCLEAR(a, b) ((a)[BITSLOT(b)] amp;= ~BITMASK(b))
#define BITTEST(a, b) ((a)[BITSLOT(b)] amp; BITMASK(b))
#define BITNSLOTS(nb) ((nb CHAR_BIT - 1) / CHAR_BIT)
enum en {
en_A,
en_B,
en_NUM
};
static unsigned char debug_en[BITNSLOTS(en_NUM)];
static void foo(const enum en en) {
assert(!BITTEST(debug_en, en)
amp;amp; (BITSET(debug_en, en), 1));
}
int main(void) {
foo(en_A);
foo(en_B);
foo(en_A);
return 0;
}
Это приводит к сбойному прерыванию утверждения на втором foo(en_A)
. Однако это не тестирование во время компиляции.