С++ метапрограммирование - генерация ошибок в коде
Можно ли создать функцию, которая принимает параметр шаблона int
, и эта функция дает ошибку времени компиляции, если значение, переданное функции, меньше 10?
Следующий код не работает, но он показывает, чего я хочу выполнить:
template <int number1>
void reportErrorIfLessThan10()
{
#if(number1 < 10)
#error the number is less than 10
#endif
}
int maint(int argc, char**argv)
{
reportErrorIfLessThan10<5>();//report an error!
reportErrorIfLessThan10<12>();//ok
return 0;
}
Ответы
Ответ 1
Если вы не хотите Boost С++ Libraries магии и хотите голые кости...
template<bool> class static_check
{
};
template<> class static_check<false>
{
private: static_check();
};
#define StaticAssert(test) static_check<(test) != 0>()
Затем используйте StaticAssert. Это #define для меня, потому что у меня есть код, который нужно запускать во многих средах, где С++ не работает правильно для шаблонов, и мне нужно просто отложить его до утверждения runtime.: (
Кроме того, это не лучшие сообщения об ошибках.
Ответ 2
template <int number1>
typename boost::enable_if_c< (number1 >= 10) >::type
reportErrorIfLessThan10() {
// ...
}
Вышеупомянутый enable_if
, без _c, потому что у нас обычный bool, выглядит так:
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };
Boost enable_if
принимает не простой bool, поэтому у них есть другая версия с добавлением _c, которая принимает простые bools, Вы не сможете вызвать его для number1
< 10. SFINAE исключает этот шаблон в качестве возможных кандидатов, потому что enable_if
не будет выставлять тип ::type
, если условие оценивается как false
. Если вы хотите, по какой-либо причине, проверить его в функции, то если у вас есть функция С++ 1x, вы можете использовать static_assert
:
template <int number1>
void reportErrorIfLessThan10() {
static_assert(number >= 10, "number must be >= 10");
}
Если нет, вы можете использовать BOOST_STATIC_ASSERT:
template <int number1>
void reportErrorIfLessThan10() {
BOOST_STATIC_ASSERT(number >= 10);
}
Единственный способ отображения дескриптивного сообщения - использовать static_assert. Вы можете более или менее имитировать это, используя типы с именами, которые описывают условие ошибки:
namespace detail {
/* chooses type A if cond == true, chooses type B if cond == false */
template <bool cond, typename A, typename B>
struct Condition {
typedef A type;
};
template <typename A, typename B>
struct Condition<false, A, B> {
typedef B type;
};
struct number1_greater_than_10;
}
template <int number1>
void reportErrorIfLessThan10() {
// number1 must be greater than 10
sizeof( typename detail::Condition< (number1 >= 10),
char,
detail::number1_greater_than_10
>::type );
}
Он печатает здесь:
error: недопустимое приложение 'sizeof' для неполного типа 'detail:: number1_greater_than_10'
Но я думаю, что самый первый подход, используя enable_if
, сделает это. Появится сообщение об ошибке, не объявленном reportErrorIfLessThan10
.
Ответ 3
Если по какой-то причине вы не можете использовать Boost, этот пример тривиально написан следующим образом:
template <int number1>
void reportErrorIfLessThan10()
{
typedef char number1_gt_10[number1 > 10 ? 1 : -1];
}
int maint(int argc, char**argv)
{
reportErrorIfLessThan10<5>();//report an error!
reportErrorIfLessThan10<12>();//ok
return 0;
}
Или более общий
#define static_assert(test, message) typedef char static_assert_at_ ## __LINE__[(test) ? 1 : -1];
Я не конкатенирую само сообщение об ошибке, потому что я чувствую, что static_assert(true, "some message");
более читаем, чем сказать static_assert(true, some_message);
. Однако это ограничивает использование прецедента только одним утверждением в строке.
Ответ 4
litb и Джо уже дали ответы, используемые на практике. Чтобы проиллюстрировать, как это можно сделать вручную, специализируясь на рассматриваемом номере (а не на общем булевом условии):
template <int N>
struct helper : helper<N - 1> { };
template <>
struct helper<10> { typedef void type; };
template <>
struct helper<0> { }; // Notice: missing typedef.
template <int N>
typename helper<N>::type error_if_less_than_10() {
}
int main() {
error_if_less_than_10<10>();
error_if_less_than_10<9>();
}
Функции нельзя унаследовать, но классы (и структуры) могут. Следовательно, этот код также использует структуру, которая автоматически и динамически генерирует случаи для всех N, кроме 10 и 0, которые начинаются с жесткой кодировки рекурсии.
Кстати, приведенный выше код действительно дает довольно приятные сообщения об ошибках:
x.cpp:16: error: no matching function for call to 'error_if_less_than_10()'