C ++将一组值映射到类型

时间:2019-03-15 13:05:12

标签: c++ c++11 templates

我的最终目标是实现从一组两个字符串到对应数据类型的映射:

"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

随着添加更多的价值映射,上述嵌套会变得非常讨厌。

是否有更好/更清洁/可行的整体方法来实现此映射?我对STEP 2以及是否有减少嵌套的方法特别感兴趣。

我正在使用C ++ 11。 (但是,如果唯一的答案是C ++ 14或更高版本,那么至少知道它会很好)

2 个答案:

答案 0 :(得分:2)

由于std::map没有常量表达式,因此无法在编译时评估模板参数str_map["BB"]

整数映射为类型的一种简单且可维护的方法将使用std::tuplestd::tuple_element,如下所示。 例如,StrType<0>charStrType<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中将字符串映射为整数。 首先,本文中的accepted answer可以在编译时比较字符串。 其次,我们可以在编译时评估中使用三元运算符。 因此,至少下面的函数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 :(得分:0)

我最近正在从事类似的工作。我提出的解决方案是这样的(有很多东西,但这里是主要思想):

//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]

主要思想是为每种类型创建一个具有正确ID的新type_id_define并将其用作选择正确执行函数的索引。另外,在生成ID时,它存储了一个用于将void*转换为给定类型的函数(我将void *用于将不同类型的函数存储在同一向量上)之后,您可以使用std :: any,void *或其他任何类型想要将对象传递给函数并获得类型安全性。

如果您想使用类似的方法,我也建议您考虑使用更好的寄存器类型方法并添加相应的功能。