Метапрограммирование C++ - создание ошибок в коде

Есть ли способ создать функцию, которая принимает параметр шаблона 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;
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
2 166
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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> { };

enable_ifУвеличение принимает не простой логический тип, поэтому у них есть другая версия с добавленным _c, которая принимает простые логические значения. Вы не сможете вызвать его для number1 <10. СФИНАЕ исключит этот шаблон как возможных кандидатов, потому что 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 ); 
}

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

error: invalid application of 'sizeof' to incomplete type 'detail::number1_greater_than_10'

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

Ответ принят как подходящий

Если вам не нужна магия Библиотеки Boost C++ и вам нужны голые кости ...

template<bool> class static_check
{
};

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

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

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

Также не самые лучшие сообщения об ошибках.

Вы всегда можете использовать typedef с отрицательным суб-скриптом, как я опубликовал, если вы не можете использовать шаблоны. Что-то вроде #define StaticAssert (test) typedef char static_assert ## __LINE __ [(test)? -1: 1];

Jasper Bekkers 20.01.2009 01:45

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

Просто помните, что у большинства компиляторов есть ограничение на глубину рекурсии, которую вы делаете. В прошлый раз, когда я проверил, GCC по умолчанию установил 50 или около того. Однако я не знаю, верно ли то же самое для этого типа наследования.

Jasper Bekkers 20.01.2009 01:55

Что ж, этот тест линейный. То есть вы получите экземпляры для каждого N от вашего ввода до 10 или 0. Поэтому, если вы установите N = 20000, вы получите 19990 базовых классов. Однажды я разбил свою операционную систему, пытаясь создать слишком много экземпляров шаблонов. как минимум требуется -ftemplate-depth-XXX :)

Johannes Schaub - litb 20.01.2009 01:55

Да, как я уже сказал, этот код больше для иллюстративных целей, чем для чего-либо еще. Нет никаких причин (технических или иных) предпочесть это решение другим.

Konrad Rudolph 20.01.2009 02:09

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

Границы массива необходимо переместить в конец строки. Также точка с запятой.

Konrad Rudolph 20.01.2009 01:30

После всех этих лет C++ у меня все еще возникают проблемы с обычным typedef. Фу.

Jasper Bekkers 20.01.2009 01:37

Этот код #define мне не подходит: компилятор проверяет синтаксис на ложные ветки. Метод специализации шаблона работает лучше.

A Fog 27.04.2012 16:25

Другие вопросы по теме