c ++ 11 — функция constexpr в C ++ для тестирования макросов препроцессора

В моем проекте C ++ (я использую autotools) у меня есть класс с begin() а также end() функции-члены, и я хочу дополнительно включить cbegin() а также cend(), если и только если поддерживается C ++ 11.

Я тестирую поддержку C ++ 11, используя файл M4 из архива Autoconf. Это макрос, который определяет HAVE_CXX11, если поддерживается, в противном случае он не определяет его.

C способ сделать то, что я хочу, это:

#ifdef HAVE_CXX11
const_iterator cbegin () const;
const_iterator cend () const;
#endif

Но я хочу сделать некоторые вещи в C ++. В этом случае я могу использовать std::enable_if опционально разрешить cbegin а также cend, Как это:

#ifdef HAVE_CXX11
#define MY_HAVE_CXX11 true
#else
#define MY_HAVE_CXX11 false

constexpr bool have_cxx11 ()
{
return MY_HAVE_CXX11;
}

/* Now use have_cxx11() with std::enable_if */

Это прекрасно работает с одним конкретным макросом, но что, если я хочу автоматизировать это для любого данного макроса? Другими словами, я хочу получить логическое значение, указывающее, определен ли данный макрос.

Я вижу два варианта:

  1. Иметь конкретную функцию для каждого макроса
  2. Иметь единственную функцию

Пример для 1:

Когда autoconf определяет свою переменную HAVE_CXX11, он также определяет have_cxx11 () для возврата true или false в зависимости от того, определена ли переменная autoconf HAVE_CXX11 (не макропостоянная) в 0 или 1.

Пример для 2:

Может быть функцией macro_is_defined() который возвращает логическое значение, например macro_is_defined(HAVE_CXX11) вернусь true когда я строю свой проект C ++ 11.

Я пытался найти способ реализовать эти идеи на чистом C ++, но не нашел ни одного. Мне пришлось сделать одну строку кода развернутой в блок директив препроцессора, что IIRC невозможно. Что я должен делать? И это хорошая идея, чтобы попробовать делать вещи C ++, как я пытаюсь, или это слишком много?

РЕДАКТИРОВАТЬ:

AutoHeader создает #undefдля всех макросов, даже для тех, которые в конечном итоге закомментированы. Поэтому я мог бы написать скрипт, который сканирует config.h и генерирует функцию constexpr для каждого макроса. Вопрос в том, где он должен быть вставлен в процесс сборки и как он называется.

1

Решение

autoheader создает #undefs для всех макросов, даже тех, которые в конечном итоге получают
закомментирован. Так что я мог бы написать скрипт, который сканирует config.h и генерирует
Функция constexpr для каждого макроса. Вопрос в том, куда его нужно вставить
в процессе сборки и как это называется.

Если вы удовлетворены (как я думаю, что вы должны), что вы не можете выполнить
цели чисто в C ++ и потребуется некоторая поддержка сборки в соответствии с цитатой, то
возможно, вы видите одно осложнение, которого там нет.

Я не вижу, что вам нужно функция, либо один за HAVE_XXXX-macro
или один на всех HAVE_XXXX-macros, чтобы сказать вам, является ли данный макрос
определены. Я имею в виду, я не вижу причин, почему вы должны начать думать об этом:

#ifdef HAVE_XXXX
#define MY_HAVE_XXXX true
#else
#define MY_HAVE_XXXX false
#endif

constexpr bool have_xxxx ()
{
return MY_HAVE_XXXX;
}

может быть автоматизирован для всех HAVE_XXXX-macros. Вы можете заменить это либо в
.h файл или .cpp с:

#ifdef HAVE_XXXX
bool const have_XXXX = true;
#else
bool const have_XXXX = false;
#endif

В C ++ const объекты имеют внутреннюю связь по умолчанию, поэтому даже в заголовке
файл этих определений не рискует множественных ошибок определений во время ссылки.

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

#ifdef HAVE_XXXX
bool const have_xxxx = true;
#else
bool const have_xxxx = false;
#endif

для каждого HAVE_XXXX,

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

-include config_aux.h

Увидеть этот ответ

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

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

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

Но вы не хотите, чтобы ваша программа ничего не делать если
статически отключенная функция-член вызывается. Вы хотите такой звонок
вызвать ошибку компиляции. Например. ты не хочешь звонить T::cbegin()
быть неоператором, когда HAVE_CXX11 ложно: вы хотите, чтобы вызов был
ошибка компиляции.

Так что вам не нужен SFINAE, но вам нужна функция-член, чтобы стать
функция-член шаблона, потому что разрешение шаблона является единственным
механизм для статического включения или отключения его в том же классе
(не считая старого #ifdef путь).

Может показаться, что очевидное решение иллюстрируется
в следующей программе:

#include <type_traits>

#define HAVE_XXXX 1 // Pretend we get this from autoconf

// Pretend we get this from config_aux.h
#ifdef HAVE_XXXX
bool const have_xxxx = true;
#else
bool const have_xxxx = false;
#endif

struct X
{
void a(){}

template<typename R = typename std::enable_if<have_xxxx,void>::type>
R b() {}
};int main()
{
X x;
// x.b();
return 0;
}

С HAVE_XXXX определил, что компилирует, реализуя оба X::a а также
X::b, Это мог сделать закомментированный вызов X::b,
Но если HAVE_XXXX мы не определил тогда любой вызов X::b
потребует создания экземпляра этой функции-члена с std::enable_if<false,void>
и это не будет компилироваться.

Но просто отредактируйте программу так, чтобы HAVE_XXXX является не определил и перестроил его.

Сборка не удалась:

ошибка: «type» в «struct std :: enable_if» не называет тип

хотя программа по-прежнему не звонит X::b, Вы не можете построить
программа вообще если HAVE_XXXX определено.

Проблема здесь в том, что компилятор Можно всегда
оценивать have_xxxx без обращения к шаблону разрешения. Так оно и есть;
так что всегда обнаруживает эту ошибку.

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

!std::is_same<R,R>::value || have_xxxx

может прийти в голову. Но:

template<
typename R =
typename std::enable_if<(!std::is_same<R,R>::value || have_xxxx),void>::type
>
R b() {}

не будет компилироваться каким-либо образом по элементарной причине, что он пытается
использовать параметр шаблона R определить тип по умолчанию для себя.

Но, как std::enable_if имеет это неуместное препятствие, зачем вообще к нему прибегать?
static_assert подходящего состояния в теле X::b будет просто хорошо.
Диагностически это будет лучше. Поэтому вместо определения X::b лайк:

    template<typename R = void>
R b() {
static_assert(!std::is_same<R,R>::value || have_xxxx,
"X::b is unimplemented without XXXX support");
}

Теперь программа будет компилировать HAVE_XXXX определяется или нет и
когда он определен, вы также можете раскомментировать X::b, Но
если HAVE_XXXX является не определено, и вы также раскомментируете X::b,
затем создается функция-член; R определен; состояние
static_assert оценен, найден ложным, а static_assert
пожары. Это результат, который вы хотите.

3

Другие решения

кажется, вы неправильно поняли, когда std::enable_if может быть использован.
использовать его, вашу функцию (или класс) должен будь шаблоном! так что вы не может используйте этот трюк для не шаблонных функций. и я думаю cbegin() а также cend() не нужно быть шаблонами …

на самом деле, единственное общее решение — это как-то использовать препроцессор. и обычно он используется так, как вы упомянули в своем первом фрагменте кода. так что, ИМО, ты перегружен здесь 🙂

и, кстати, это не нужно, чтобы определить constexpr функции … все, что вам действительно нужно, это определить какой-то тип (структура) public из std::true_type или же std::false_type (или более общий способ просто использовать std::integral_constant<bool, (some-boolean-expression-here)>), поэтому его можно использовать как enable_if условие (путем доступа к вложенному value членом).

0

Тестирование для C++11 в переносном виде это делается следующим образом

#if __cplusplus >= 201103L

/* C++11 stuff */

#endif

вместо того, чтобы использовать специфические для среды макросы, такие как HAVE_CXX11,

Я не думаю, что вы можете автоматизировать создание метода, который обеспечивает языковой интерфейс для вашего макроса препроцессора, по крайней мере, если условие имеет вид #ifdef MACROпотому что препроцессор не достаточно мощный для такого рода вещей.

0
По вопросам рекламы [email protected]