C 将一组值映射到类型

C++ Mapping a set of values to types

本文关键字:映射 类型 一组      更新时间:2023-10-16

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

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

我可以提出的最好的"不完全有效"的解决方案是两个部分。第一部分是使用std ::映射在字符串和相应的枚举值之间映射。第二部分使用模板类型别名和std ::条件将枚举值映射到类型。

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或以上,那么至少要注意它会很不错)

由于 std::map没有constexpr ctors,因此无法在编译时评估您的模板参数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中的整数。首先,可以在本文中被接受的答案在编译时比较字符串。其次,我们可以在编译时间评估中使用三元运算符。因此,至少以下函数getIdx可以在编译时将每个字符串映射到相应的整数。例如,getIdx("AA")为零:

constexpr bool strings_equal(const char* a, const char* b) {
    return *a == *b && (*a == '' || 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.

我最近从事这样的事情。我提出的解决方案是这样的(有更多内容,但这里是主要想法):

//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时,IT在void*上存储了一个用于给定类型的功能(我使用void*用于同一向量上的其他类型函数)之后,您可以使用std :: any,void*或任何您想要的任何内容对象对功能并获得类型安全。

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