C ++ Отображение набора значений в типы - PullRequest
1 голос
/ 15 марта 2019

Моя конечная цель - добиться сопоставления набора строк из двух символов с соответствующими типами данных:

"AA" --> char
"BB" --> int
"CC" --> float
"DD" --> std::complex<double>
and so on ...

Лучшее «не совсем работающее» решение, которое я могу придумать, состоит из двух частей.Первая часть использует std :: map для отображения между строками и соответствующими значениями перечисления.Вторая часть использует шаблонный псевдоним типа и std :: conditional для сопоставления значений перечисления с типами.

enum class StrEnum { AA, BB, CC, DD };
// STEP 1: string --> enum
// !! std::map will not work here since a constexpr is required !!
std::map<std::string, StrEnum> str_map = {
    {"AA", StrEnum::AA},
    {"BB", StrEnum::BB},
    {"CC", StrEnum::CC},
    {"DD", StrEnum::DD}
};
// STEP 2: enum --> type
template<StrEnum val> using StrType = typename std::conditional<
   val == StrEnum::AA,
   char,
   typename std::conditional<
       val == StrEnum::BB,
       int,
       typename std::conditional<
           val == StrEnum::CC,
           float,
           std::complex<double>
       >::type
   >::type
>::type;

Использование цели: StrType<str_map["BB"]> myVal; // <-- does not work in current state with std::map

По мере добавления дополнительных отображений значений вышеВложение может стать очень неприятным.

Есть ли лучший / более чистый / эффективный способ достижения этого отображения?Меня особенно интересует ШАГ 2 и есть ли способ уменьшить вложенность.

Я использую C ++ 11.(но если единственный ответ лежит в C ++ 14 или выше, было бы неплохо хотя бы об этом знать)

Ответы [ 2 ]

2 голосов
/ 15 марта 2019

Поскольку std::map не имеет аргументов constexpr, аргумент шаблона str_map["BB"] не может быть оценен во время компиляции.

Простой и понятный способ сопоставления целых чисел с типами будет использовать std::tuple и std::tuple_element следующим образом. Например, StrType<0> равно char, StrType<1> равно int и т. Д.

using types = std::tuple<char, int, float, std::complex<double>>;

template<std::size_t N>
using StrType = typename std::tuple_element<N, types>::type;

Тогда проблема в том, как сопоставить строки с целыми числами в C ++ 11. Во-первых, строки можно сравнить во время компиляции с помощью принятого ответа в этом посте. Во-вторых, мы можем использовать троичный оператор в вычислениях во время компиляции. Таким образом, по крайней мере, следующая функция getIdx может сопоставить каждую строку с соответствующим целым числом во время компиляции. Например, getIdx("AA") равен нулю:

constexpr bool strings_equal(const char* a, const char* b) {
    return *a == *b && (*a == '\0' || strings_equal(a + 1, b + 1));
}

constexpr std::size_t getIdx(const char* name) 
{
    return strings_equal(name, "AA") ? 0:
           strings_equal(name, "BB") ? 1:
           strings_equal(name, "CC") ? 2:
           strings_equal(name, "DD") ? 3:
                                       4; // compilation error
}

Вы можете использовать эти функции для текущей цели следующим образом:

DEMO

StrType<getIdx("BB")> x; // x is int.

constexpr const char* float_type = "CC";
StrType<getIdx(float_type)> y; // y is float.

static_assert(std::is_same<StrType<getIdx("AA")>, char> ::value, "oops."); // OK.
static_assert(std::is_same<StrType<getIdx("BB")>, int>  ::value, "oops."); // OK.
static_assert(std::is_same<StrType<getIdx("CC")>, float>::value, "oops."); // OK.
static_assert(std::is_same<StrType<getIdx("DD")>, std::complex<double>>::value, "oops."); // OK.
1 голос
/ 15 марта 2019

Я недавно работал над чем-то подобным. Мое предлагаемое решение было примерно таким (с гораздо большим количеством материала, но здесь основная идея):

//Define a Type for ID
using TypeIdentifier = size_t;

//Define a ID generator
struct id_generator {
    static TypeIdentifier create_id() {
        static TypeIdentifier value = 0;
        return value++;
    }
};

//Define some kind of handler for place 
struct type_id_handler {
    static std::vector<std::function<void(void*)>> type_handler;
};

std::vector<std::function<void(void*)>> type_id_handler::type_handler;

//Define id's and make a basic functions
template<typename T>
struct type_id_define {
    static TypeIdentifier get_id() {
        static TypeIdentifier id = id_generator::create_id();
        static auto one_time_stuff = [] () -> bool {
            type_id_handler::type_handler.resize(id+1);
            type_id_handler::type_handler[id] = [](void* ptr) {
                auto * object = static_cast<T*>(ptr);
                //do stuff your type
                std::cout << __PRETTY_FUNCTION__ << std::endl;
            };
            return true;
        }();

        return id;
    }
};

Для проверки основного манекена:

int main() {

    std::map<std::string, TypeIdentifier> typeMap {
            {"AA", type_id_define<char>::get_id()},
            {"BB", type_id_define<int>::get_id()},
    };

    int a;
    char b;
    type_id_handler::type_handler[typeMap["BB"]](&a);
    type_id_handler::type_handler[typeMap["AA"]](&b);
    return 0;
}

Вывод должен быть:

type_id_define<T>::get_id()::<lambda()>::<lambda(void*)> [with T = int]
type_id_define<T>::get_id()::<lambda()>::<lambda(void*)> [with T = char]

Основная идея - создать новый type_id_define с правильным идентификатором для каждого типа и использовать его в качестве индекса для выбора правильной функции для выполнения. Также при генерации идентификатора он сохраняет функцию для приведения из void* к данному типу (я использую void * для сохранения функции другого типа на одном и том же векторе). После этого вы можете использовать std :: any, void * или все, что вы хотите для передать объект в функцию и получить безопасность типов.

Если вы хотите использовать что-то подобное, я также рекомендую подумать о лучшем способе для типов регистров и добавить соответствующую функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...