В моем проекте 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:
Когда 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 для каждого макроса. Вопрос в том, где он должен быть вставлен в процесс сборки и как он называется.
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
пожары. Это результат, который вы хотите.
кажется, вы неправильно поняли, когда std::enable_if
может быть использован.
использовать его, вашу функцию (или класс) должен будь шаблоном! так что вы не может используйте этот трюк для не шаблонных функций. и я думаю cbegin()
а также cend()
не нужно быть шаблонами …
на самом деле, единственное общее решение — это как-то использовать препроцессор. и обычно он используется так, как вы упомянули в своем первом фрагменте кода. так что, ИМО, ты перегружен здесь 🙂
и, кстати, это не нужно, чтобы определить constexpr
функции … все, что вам действительно нужно, это определить какой-то тип (структура) public из std::true_type
или же std::false_type
(или более общий способ просто использовать std::integral_constant<bool, (some-boolean-expression-here)>
), поэтому его можно использовать как enable_if
условие (путем доступа к вложенному value
членом).
Тестирование для C++11
в переносном виде это делается следующим образом
#if __cplusplus >= 201103L
/* C++11 stuff */
#endif
вместо того, чтобы использовать специфические для среды макросы, такие как HAVE_CXX11
,
Я не думаю, что вы можете автоматизировать создание метода, который обеспечивает языковой интерфейс для вашего макроса препроцессора, по крайней мере, если условие имеет вид #ifdef MACRO
потому что препроцессор не достаточно мощный для такого рода вещей.