Есть ли способ избежать макросов препроцессора при получении аргумента от компилятора? - PullRequest
1 голос
/ 14 февраля 2020

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

Затем вы можете использовать его с директивами препроцессора. например:

#ifndef MAX_SIZE
    constexpr auto maxSize = 42; // Some default value if no MAX_SIZE is specified
#else
    constexpr auto maxSize = MAX_SIZE;
#endif

Чтобы установить максимальный размер при компиляции с помощью g cc, вы можете скомпилировать код с параметром -DMAX_SIZE=<desired_size>.

. он включает использование макросов препроцессора для получения аргумента MAX_SIZE от компилятора. Макросы препроцессора считаются злыми по многим причинам (я не буду вдаваться в них, потому что это не главное).

Есть ли способ достичь этой функциональности без использования макросов препроцессора? (У меня есть до C ++ 20, так что не стесняйтесь go дико использовать ваши решения - в основном, некоторые из них еще не реализованы g cc 10 )

Ответы [ 2 ]

1 голос
/ 14 февраля 2020

Есть и другие способы, но вы, вероятно, не хотите их использовать. То, что вы делаете, это хорошее использование препроцессора. Хотя я бы написал что-то вроде:

#ifndef MAX_SIZE
#define MAX_SIZE 42
#endif
constexpr size_t maxSize = MAX_SIZE;

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

#indef MAX_SIZE
#error "You need to define MAX_SIZE to compile this code, e.g. -DMAX_SIZE=42"
#endif

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

Но есть и другие способы, и можно избежать макросов препроцессора!

Генерация самого исходного кода. Хотя это может показаться сложным и часто таковым, существуют способы сделать его менее сложным. Структурируйте код так, чтобы часть, которая должна быть сгенерирована, была маленькой. Например, получить другие значения из одного определения maxSize вместо генерации всего кода, который должен знать размер. Есть также системы, которые уже могут делать это в некоторых случаях. Например, если кто-то использует CMake, создайте файл header.h.in следующим образом:

constexpr size_t maxSize = @MAXSIZE@;

, а затем поместите его в файл CMakeLists.txt:

set(MAXSIZE 42)
configure_file(header.h.in header.h @ONLY ESCAPE_QUOTES)

Когда проект построен, cmake превратит header.h.in в header.h с @MAXSIZE@, измененным на 42. При этом не использовался препроцессор C ++ , но мы эффективно использовали CMake препроцессор для предварительной обработки файла перед его компиляцией, так что же, он отличается? Это просто другой язык препроцессора (который не так хорош, как язык препроцессора C / C ++).

Другой способ - с использованием констант времени соединения. Символ компоновщика обычно является именем функции или глобальной переменной. Что-то со стати c срок хранения. Значением символа является адрес объекта. Но в командах компоновщику можно определить любой символ, который вы хотите. Вот пример C file:

#include <stdio.h>
char array1[1];
extern array2[];
int main(void) { printf("%p %p\n", array1, array2); return 0; }

Скомпилируйте с g cc как gcc example.c -Wl,--defsym=array2=0xf00d.

Так же, как он печатает адрес array1, он напечатает 0xf00d как адрес array2. И поэтому мы ввели константу в наш код без использования какого-либо препроцессора, ни C, ни CMake.

Но это значение не известно компилятору, только компоновщику. Это не «целочисленное константное выражение», и его нельзя использовать в определенных местах, таких как метки регистра или размер объекта с длительностью хранения stati c. Потому что компилятор должен знать точное значение тех, которые компилируют код. Компилятор сгенерировал код для вызова printf, не зная точного значения array1 или array2. Это не могло сделать это для метки выписки. Это действительно так, хотя «целочисленные константные выражения» существуют в стандартах C / C ++ и не совпадают с выражениями, которые являются константными и имеют целочисленный тип.

1 голос
/ 14 февраля 2020

Нет другого механизма, основанного на компиляторе, кроме определения макроса в G CC для параметрической компиляции, о котором я знаю.

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

Недостатком этого выбора является сложность построения из-за дополнительного шага. Этот подход может быть использован для обхода проблем препроцессора за счет возможного введения новых проблем пользовательского процессора.

...