Как определить тип данных из пользовательского ввода в C ++? - PullRequest
0 голосов
/ 02 ноября 2019

У меня есть класс HashMap:

template<typename K, typename T>
class HashNode
{
 ...
};

template<typename K, typename T>
class HashMap
{
...
};

Я хочу получить пользовательский тип данных для HashMap. Например:

Проблемы пользователя в командной строке: 'IS'

, что означает int для ключа и строку для значения

И как результат:

HashMap<int, string>*my_map = new HashMap<int, string>(capacity);

Я пытался решить эту проблему с помощью команды switch и reinterpret_cast <>, но это выглядело слишком многословно.

switch ((int)typename1)
{
    case('I'):
        switch((int)typename2)
        {
            case('I'):
                HashMap<int, int>*table1 = new HashMap<int, int>(20);
                return table1;
            case('D'):
                HashMap<int, double>*table2 = new HashMap<int, double>(20);
                return table2;
         ...
}

Какая для этого альтернатива?

Спасибо

1 Ответ

1 голос
/ 07 ноября 2019

Это сверхинженерия, но я готов принять вызов сегодня ...

Итак, вы хотите вернуть один из N типов хеш-карт, выбранных на основе пользовательского ввода? std::variant - правильный тип для работы. variant<A,B,C> - это тип суммы, который может содержать один из типов A, B или C. Он был добавлен в C ++ 17, но вы можете найти реализации с одним заголовком, которые работают в C ++ 11, например this .

Как вы выяснили, шаблоны - лучший способсоздать шаблон. Похоже, вы хотите выбрать тип значения карты хеш-функции на основе символа, поэтому давайте дадим себе способ определить отображение в шаблоне:

template <char C, typename TValue>
struct Opt { };

Тогда наша цель - сгенерировать ваш переключатель, перечисливкуча этих опций и передача пользовательского ввода:

auto table = CreateHashMap<Opt<'i', int>, Opt<'d', double>, /*etc*/>(user_input);

Где таблица

std::variant<std::monostate, HashMap<int, int>, HashMap<int, double>, /*etc*/>

(Первая опция, std::monostate, это «пустая» опция, если функцияне удается)

И сгенерированный код:

std::variant<std::monostate, HashMap<int, int>, HashMap<int, double>/*, etc */> CreateHashMap(char user_input) {
    switch (user_input) {
        case 'i':
            return HashMap<int, int>(60);
        case 'd':
            return HashMap<int, double>(60);
        /* case 'e': return etc; */
        default:
            return std::monostate{};
    }
}

Шаг 1 - вывести тип варианта из списка параметров

Это можно сделать, начиная с std::variant<std::monostate>, а затем рекурсивно добавляете типы один за другим:

template <typename... TOpts>
struct OptToVariantImpl;

// Recursive case - pass TValue to the variant
template <typename... TValues, char Code, typename TValue, typename... TOpts>
struct OptToVariantImpl<std::variant<TValues...>, Opt<Code, TValue>, TOpts...> 
    : OptToVariantImpl<std::variant<TValues..., HashMap<int, TValue>>, TOpts...> { };

// Terminal case - just expose the variant
template <typename TVariant>
struct OptToVariantImpl<TVariant> {
    using Type = TVariant;
};

// Initial variant just has an empty state.
template <typename... TOpts>
using OptToVariant = typename OptToVariantImpl<std::variant<std::monostate>, TOpts...>::Type;

Шаг 2 - Генерация переключателя

Наш «переключатель» для каждого параметра прост, поскольку все параметры дают одинаковый результат,вариант. Вы можете объединить их в одну большую рекурсивную троицу:

return user_input == 'Code' ? return /*HashMapForThisOpt*/ : /*Pass the buck*/; 

Умный компилятор O3 должен иметь возможность встроить это и остаться с блоком переключателей.

Заполненный код:

template <typename... TValues>
struct CreateHashMapImpl;

// Recursive case - Test code and return hashcode or evaluate next option
template <typename TVariant, char Code, typename TValue, typename... TOpts>
struct CreateHashMapImpl<TVariant, Opt<Code, TValue>, TOpts...> 
    : CreateHashMapImpl<TVariant, TOpts...> {
    constexpr TVariant operator()(char code) {
        return code == Code ? TVariant { HashMap<int, TValue>(60) } : CreateHashMapImpl<TVariant, TOpts...>::operator()(code);
    };
};

// Terminal case - return std::monostate for failure result.
template <typename TVariant>
struct CreateHashMapImpl<TVariant> {
    constexpr TVariant operator()(char code) { return TVariant { std::monostate{} }; }
} ;

template <typename... TOpts>
OptToVariant<TOpts...> CreateHashMap(char code) {
    return CreateHashMapImpl<OptToVariant<TOpts...>, TOpts...>{}(code);
}

Шаг 3 - Используйте

auto table = CreateHashMap<Opt<'i', int>, Opt<'d', double>, Opt<'c', char>>(user_input);
if (auto* result = std::get_if<HashMap<int, double>>(&table)) {
    std::cout << "double: " << result->find(42)->second;
}

Соберите все вместе: https://godbolt.org/z/VBnMq2

Вы можете увидеть пример, оптимизирующий до простого переключателя.

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