Компилятор C утверждает - как реализовать?

Возможно, вы имеете в виду:

let str = "2.1"
if let double = Double(str) as? NSNumber {
    let formatter = NumberFormatter()
    formatter.decimalSeparator = "."
    formatter.minimumFractionDigits = 2
    if let myNewStringValue = formatter.string(from: double) {
        print(myNewStringValue) // 2.10
    }
}
41
задан Trevor Hickey 17 February 2017 в 19:12
поделиться

8 ответов

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

Ключевой трюк заключается в том, чтобы найти конструкцию, которая может быть оценена во время компиляции и может вызвать ошибку для некоторых значений. Один из ответов - объявление массива не может иметь отрицательный размер. Использование typedef предотвращает выделение места при успехе и сохраняет ошибку при сбое.

Само сообщение об ошибке будет зашифровано ссылаться на объявление отрицательного размера (GCC говорит, что «размер массива foo отрицателен»), поэтому вы должны выбрать имя для типа массива, которое намекает на то, что эта ошибка действительно является проверкой утверждения.

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

Мое обычное решение состояло в том, чтобы макрос имел два параметра. Первое - это условие для утверждения true, а второе - часть имени типа, объявленного за кулисами. Ответ плинтуса намекает на использование вставки токена и предопределенного макроса __ LINE __ для формирования уникального имени, возможно, без дополнительного аргумента.

К сожалению, если проверка подтверждения находится во включенном файле, она все равно может столкнуться с проверкой по тому же номеру строки во втором включенном файле или по этому номеру строки в основном исходном файле. Мы могли бы описать это с помощью макроса __ FILE __ , но он определен как строковая константа, и не существует трюка препроцессора, который может превратить строковую константу обратно в часть имени идентификатора; не говоря уже о том, что допустимые имена файлов могут содержать символы, которые не являются допустимыми частями идентификатора.

Итак, я бы предложил следующий фрагмент кода:

/** A compile time assertion check.
 *
 *  Validate at compile time that the predicate is true without
 *  generating code. This can be used at any point in a source file
 *  where typedef is legal.
 *
 *  On success, compilation proceeds normally.
 *
 *  On failure, attempts to typedef an array type of negative size. The
 *  offending line will look like
 *      typedef assertion_failed_file_h_42[-1]
 *  where file is the content of the second parameter which should
 *  typically be related in some obvious way to the containing file
 *  name, 42 is the line number in the file on which the assertion
 *  appears, and -1 is the result of a calculation based on the
 *  predicate failing.
 *
 *  \param predicate The predicate to test. It must evaluate to
 *  something that can be coerced to a normal C boolean.
 *
 *  \param file A sequence of legal identifier characters that should
 *  uniquely identify the source file in which this condition appears.
 */
#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)

#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
    typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];

Типичное использование может выглядеть примерно так:

#include "CAssert.h"
...
struct foo { 
    ...  /* 76 bytes of members */
};
CASSERT(sizeof(struct foo) == 76, demo_c);

В GCC Ошибка подтверждения выглядит следующим образом:

$ gcc -c demo.c
demo.c:32: error: size of array `assertion_failed_demo_c_32' is negative
$
42
ответ дан 27 November 2019 в 00:43
поделиться

Следующий макрос COMPILER_VERIFY (exp) работает довольно хорошо.

// combine arguments (after expanding arguments)
#define GLUE(a,b) __GLUE(a,b)
#define __GLUE(a,b) a ## b

#define CVERIFY(expr, msg) typedef char GLUE (compiler_verify_, msg) [(expr) ? (+1) : (-1)]

#define COMPILER_VERIFY(exp) CVERIFY (exp, __LINE__)

Он работает для обоих C и C ++ и могут использоваться везде, где разрешен typedef. Если выражение истинно, оно генерирует typedef для массива из 1 символа (что безвредно). Если выражение ложно, оно генерирует typedef для массива -1 символа, что обычно приводит к сообщению об ошибке. Выражение, данное как arugment, может быть любым, что вычисляет константу времени компиляции (поэтому выражения, включающие sizeof (), работают нормально).

7
ответ дан 27 November 2019 в 00:43
поделиться

Если ваш компилятор устанавливает макрос препроцессора, такой как DEBUG или NDEBUG, вы можете сделать что-то вроде этого (в противном случае вы можете установить это в Makefile):

#ifdef DEBUG
#define MY_COMPILER_ASSERT(EXPRESSION)   switch (0) {case 0: case (EXPRESSION):;}
#else
#define MY_COMPILER_ASSERT(EXPRESSION)
#endif

Затем ваш компилятор утверждает только для отладки строит.

4
ответ дан 27 November 2019 в 00:43
поделиться

Использование '#error' является допустимым определением препроцессора, которое приводит к остановке компиляции на большинстве компиляторов. Вы можете просто сделать это, например, чтобы предотвратить компиляцию при отладке:


#ifdef DEBUG
#error Please don't compile now
#endif
1
ответ дан 27 November 2019 в 00:43
поделиться

Я знаю, что вы заинтересованы в C, но взгляните на C ++ boost static_assert . (Между прочим, это, вероятно, станет доступным в C ++ 1x.)

Мы сделали нечто подобное, опять же для C ++:

#define COMPILER_ASSERT(expr)  enum { ARG_JOIN(CompilerAssertAtLine, __LINE__) = sizeof( char[(expr) ? +1 : -1] ) }

Это работает только в C ++, по-видимому. В этой статье обсуждается способ ее модификации для использования в C.

4
ответ дан 27 November 2019 в 00:43
поделиться

Когда вы компилируете свои последние двоичные файлы, определите MY_COMPILER_ASSERT, чтобы он был пустым, чтобы его вывод не включался в результат. Определите его только так, как вы это делаете для отладки.

Но на самом деле вы не сможете уловить каждое утверждение таким образом. Некоторые просто не имеют смысла во время компиляции (например, утверждение, что значение не является нулевым). Все, что вы можете сделать, это проверить значения других #defines. Я не совсем уверен, почему вы хотите это сделать.

2
ответ дан 27 November 2019 в 00:43
поделиться

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

Я думаю, что они там делают, это определяют массив.

 #define MY_COMPILER_ASSERT(EXPRESSION) char x[(EXPRESSION)];

Если EXPRESSION имеет значение true, он определяет char x [1]; , что в порядке. Если false, он определяет char x [0]; , что недопустимо.

-1
ответ дан 27 November 2019 в 00:43
поделиться

Лучшая запись, которую я смог найти для статических утверждений в C, - это pixelbeat . Обратите внимание, что статические утверждения добавляются в C ++ 0X и могут перейти в C1X, но это не произойдет некоторое время. Я не знаю, увеличат ли макросы в приведенной мной ссылке размер ваших двоичных файлов. Я подозреваю, что они не будут, по крайней мере, если вы компилируете на разумном уровне оптимизации, но ваш пробег может отличаться.

4
ответ дан 27 November 2019 в 00:43
поделиться
Другие вопросы по тегам:

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