Arhn - архитектура программирования

Значения constexpr C++ для типов

Я хочу иметь возможность создавать операторы переключения по идентификатору типа. Я нашел механизм, который может дать уникальный идентификатор для разных типов. Это очень просто:

template <typename T>
struct type { 
    static void id() { } 
};

template <typename T>
constexpr const size_t type_id() {
    return reinterpret_cast<size_t>(&type<T>::id); 
}

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

int main(void) {
    size_t a = type_id<int>();
    switch (a) {
    case type_id<int>():
        break;
    }
    return 0;
}

Почему не константа? Как я мог добиться такого эффекта?

Редактировать:

Могу ли я сделать что-то подобное без reinterpret_cast?


  • reinterpret_cast не может появляться в функции constexpr. Ничего не поделаешь. 15.02.2017
  • Что такое ysize? Опубликуйте минимально воспроизводимый пример. 15.02.2017
  • @LightnessRacesinOrbit Извините, ysize был моим собственным определенным size_t. Отредактировано 15.02.2017

Ответы:


1

Я не уверен, что это хорошая идея, но... просто для удовольствия... использовать счетчик constexpr, предложенный на этой странице вы должны иметь возможность заменить значение указателя.

Ниже приводится (повторяю: просто для удовольствия) полный эксперимент.

#include <iostream>

template <int N>
struct flag
 { friend constexpr int adl_flag (flag<N>); };

template <int N>
struct writer
 {
   friend constexpr int adl_flag (flag<N>)
    { return N; }

   static constexpr int value { N };
 };

template <int N, int = adl_flag (flag<N> {})>
int constexpr reader (int, flag<N>)
 { return N; }

template <int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1> {}))
 { return R; }

int constexpr reader (float, flag<0>)
 { return 0; }

template <int N = 1>
int constexpr next (int R = writer<reader (0, flag<32> {}) + N>::value)
 { return R; }

template <typename T>
struct type
 { 
   static constexpr int id { next() };

   constexpr static int type_id ()
    { return id; }
 };

void printType (int idT )
 {
   switch ( idT )
    {
      case type<int>::type_id():
         std::cout << "- int type" << std::endl;
         break;

      case type<long>::id:
         std::cout << "- long type" << std::endl;
         break;

      default:
         std::cout << "- another type" << std::endl;
         break;
    }
 }

int main ()
 {
   int ii { type<int>::id };
   int il { type<long>::type_id() };

   printType(ii);
   printType(il);
 }
15.02.2017
  • И это, и @PaperBirdMaster потрясающие, к сожалению, я не могу принять 2 ответа. Спасибо, умничка! 16.02.2017

  • 2

    constexpr функции не могут использовать reinterpret_cast ни в какой форме. Более формальное чтение можно найти по адресу http://en.cppreference.com/w/cpp/language/constant_expression

    15.02.2017

    3

    Это может решить вашу проблему:

    #include <tuple>
    
    //Index from http://stackoverflow.com/a/18063608/3484570
    template <class T, class Tuple>
    struct Index;
    
    template <class T, class... Types>
    struct Index<T, std::tuple<T, Types...>> {
        static const std::size_t value = 0;
    };
    
    template <class T, class U, class... Types>
    struct Index<T, std::tuple<U, Types...>> {
        static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
    };
    
    template <class T>
    constexpr std::size_t type_id() {
        //need to add every type in this tuple:
        return Index<T, std::tuple<int, double, char>>::value;
    }
    
    int main() {
        size_t a = type_id<int>();
        switch (a) {
            case type_id<int>():
                break;
        }
    }
    

    Хорошая новость заключается в том, что вы получаете type_id<T>(), который можно использовать в контексте constexpr, например, в case, как вы хотели.
    Плохая новость заключается в том, что вам нужно перечислить все поддерживаемые типы.
    На практике вы можете получить используется для сообщения об ошибке, которое возникает, когда вы запрашиваете type_id неподдерживаемого типа и просто добавляете его и, в конечном итоге, добавляете все соответствующие типы.

    15.02.2017

    4

    Я хотел бы предложить другой подход, который включает constexpr функции и макросы (eeeewww...):

    // Some naive text hashing function
    template <std::size_t SIZE>
    constexpr std::size_t hash(const char (&type_name)[SIZE])
    {
        std::size_t result{0xf};
        for (const auto &c : type_name)
        {
            result <<= 1;
            result |= c;
        }
    
        return result;
    }
    

    Сначала мы создаем функцию constexpr, способную преобразовать строковый литерал в число, это мой подход, но вы можете выбрать другую функцию, если она constexpr, затем мы создаем макрос, который преобразует данный параметр в строку, используя #:

    #define TYPE_ID(X) hash(#X)
    

    И теперь мы можем его использовать:

    int main(void) {
        size_t a = TYPE_ID(int);
        switch (a) {
        case TYPE_ID(int):
            break;
        }
        return 0;
    }
    

    Плюсы:

    • Довольно просто.
    • Крошечный объем кода.

    Минусы:

    • Макросы.
    • Принимает любое значение, включая ерунду: TYPE_ID(I LOVE BACON) действительно.
    • Дает разные результаты для TYPE_ID(size_t) и TYPE_ID(unsigned long), даже если они могут быть одного типа.
    16.02.2017
  • это не будет работать с типами шаблонов из-за использования макроса, оно будет фиксировать имя параметра шаблона, а не тип, который он фактически представляет 24.01.2020
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..