我的最终目标是实现从一组两个字符串到对应数据类型的映射:
"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或更高版本,那么至少知道它会很好)
答案 0 :(得分:2)
由于std::map
没有常量表达式,因此无法在编译时评估模板参数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中将字符串映射为整数。
首先,本文中的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
}
您可以将这些功能用于当前目的,如下所示:
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 *或其他任何类型想要将对象传递给函数并获得类型安全性。
如果您想使用类似的方法,我也建议您考虑使用更好的寄存器类型方法并添加相应的功能。