С++ метапрограммирование - генерация ошибок в коде

Можно ли создать функцию, которая принимает параметр шаблона 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()'