C ++ метапрограммирование - генерирование ошибок в коде - PullRequest
6 голосов
/ 20 января 2009

Есть ли способ, которым я могу создать функцию, которая принимает параметр шаблона 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;
}

Ответы [ 4 ]

7 голосов
/ 20 января 2009

Если вы не хотите Повышайте библиотеки C ++ Магия и хотите обнажиться ...

template<bool> class static_check
{
};

template<> class static_check<false>
{
private: static_check();
};

#define StaticAssert(test) static_check<(test) != 0>()

Тогда используйте StaticAssert. Это #define для меня, потому что у меня есть код, который должен запускаться во многих средах, где C ++ не работает должным образом для шаблонов, и мне нужно просто вернуть его обратно к утверждению времени выполнения. (

Кроме того, не самые лучшие сообщения об ошибках.

3 голосов
/ 20 января 2009

Если по какой-то причине вы не можете использовать 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); Однако это ограничивает вариант использования только одним утверждением на строку.

3 голосов
/ 20 января 2009
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, которая принимает простые bool. Вы не сможете вызвать его для number1 <10. <em>SFINAE исключит этот шаблон в качестве возможных кандидатов, поскольку enable_if не предоставит тип ::type, если условие оценивается как false. Если по какой-то причине вы хотите проверить это в функции, то, если у вас есть функция C ++ 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 ); 
}

Он печатает это здесь:

ошибка: неверное применение 'sizeof' к неполному типу 'detail :: number1_greater_than_10'

Но я думаю, что самый первый подход, использующий enable_if, сделает это. Вы получите сообщение об ошибке необъявленного reportErrorIfLessThan10.

0 голосов
/ 20 января 2009

Лит и Джо уже дали ответы, используемые на практике. Просто чтобы проиллюстрировать, как это можно сделать вручную, указав соответствующее число (а не общее логическое условие):

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()'
...