运行时值到类型的映射
Runtime value to type mapping
我有一个可以通过网络发送的类型列表,举个例子:
enum types {
E_T1,
E_T2,
E_T3,
E_T4
};
现在我有一个对应于每种类型的类列表,假设每种类型都声明为class E_T1 {...}
、class E_T2 {...}
等。
它们不是从公共基类派生的,这是不可能的。每个类都有一个验证方法,我需要通过网络发送数据来调用它。客户端发送数据D
和对应于消息类型的id。我需要获取与类型对应的对象。如果需要,我可以使用C++0x功能。
到目前为止,我所尝试的是为types
使用专门的模板,为与之相关的对象保存一个typedef。这显然是一个愚蠢的想法,因为模板参数需要是编译时间常数,所以不可能沿着getType<data.id()>::type
做一些事情。
然后我尝试使用Boost。获得如下常见可返回类型的变体(使用mpl向量在运行时迭代已注册的类型以进行解bug):
template <typename C>
struct getType() {
typedef C type;
}
typedef boost::mpl::vector<
getType<E_T1>,
getType<E_T2>,
getType<E_TX>...
> _types;
typedef boost::make_variant_over<_types>::type _type;
//use a map to store each type <-> id
boost::unorderd_map<types, _type> m;
m[E_T1] = getType<E_T1>();
m[data.id()]::type x; //<- access type, can now call x.validate(data)
这样做的问题是,默认情况下每个变体只能有20个条目。这可以被覆盖,但根据我的理解,应该考虑每个类型的开销,我们在这里谈论的是几千个类型。
也尝试过boost.any,但它没有任何类型的信息,所以这是不可能的。有人知道如何优雅地解决这个问题吗?寻找一种在处理类型时不必编写1k switch语句的东西。
所有类型在编译类型中都是已知的,它们对应的ID也是如此。Id->类型解析需要在运行时进行。
提前感谢,罗宾。
外部多态性(*)
这是一个广为人知的习语,但它被广泛使用:我第一次在shared_ptr
实现中遇到它,它在我的工具箱中非常有用。
这个想法实际上是为所有这些类型创建一个基类。但不能让它们直接来源于它。
class Holder {
public:
virtual ~Holder() {}
virtual void verify(unsigned char const* bytes, size_t size) const = 0;
}; // class Holder
template <typename T>
class HolderT: public Holder {
public:
HolderT(): _t() {}
virtual void verify(unsigned char const* bytes, size_t size) const {
_t.verify();
}
private:
T _t;
}; // class HolderT
template <typename T>
std::unique_ptr<Holder> make_holder() {
return std::unique_ptr<Holder>(new HolderT<T>());
}
因此,这是添加新级别间接性的经典策略。
现在,您显然需要一个从值到类的切换。或者。。。地图?
using maker = std::unique_ptr<Holder> (&)();
using maker_map = std::unordered_map<types, maker>;
std::unique_ptr<Holder> select(types const E) {
static maker_map mm;
if (mm.empty()) {
mm.insert(std::make_pair(E_T1, make_holder<EC_T1>));
// ...
}
maker_map::const_iterator it = mm.find(E);
if (it == mm.end()) { return std::unique_ptr<Holder>(); }
return (*it->second)();
}
现在你可以用多态的方式处理它们:
void verify(types const E, unsigned char const* bytes, size_t size) {
std::unique_ptr<Holder> holder = select(E);
if (not holder) { std::cerr << "Unknown type " << (int)E << "n"; return; }
holder->verify(bytes, size);
}
当然,欢迎您根据自己的需要制定不同的策略。例如,将映射从select
中移出,这样您就可以动态注册您的类型(就像插件一样)。
(*)至少这是我给它取的名字,我很高兴发现它已经被命名了。
我假设您有一种处理消息的通用方法,例如重载函数:
void handle_message(const E_T1& msg);
void handle_message(const E_T2& msg);
//...
现在,您实际上不需要获取对象的类型。您所需要的只是一种方法来处理该类型的消息,给定未编码的消息。
因此,我建议您填充一个工厂功能图:
std::unordered_map<types, std::function<void (unsigned char const* bytes, size_t size)> handlers;
handlers[E_E1] = [](unsigned char const* bytes, size_t size) { handle_message(E_T1(bytes, size)); };
// ...
然后,一旦解码了类型,就可以使用handlers[type](bytes, size)
来解码和处理消息。
尝试可变模板和您已经定义的getType类:
enum types { T1_ID, T2_ID, .... };
class T1; class T2; class T3; ....
template <types t> struct getType;
template <> struct getType<T1_ID> { typedef T1 type; };
template <> struct getType<T2_ID> { typedef T2 type; };
...
并且操作验证:
template <types...>
struct type_operation;
template <types t1, types... rest>
struct type_operation<t1, rest...>
{
void verify(types t)
{
if (t == t1)
{
typename getType<t1>::type a;
a.verify(); // read from network and verify the rest of data....
}
else type_operation<rest...>::verify(t, data);
}
};
template <>
struct type_operation<>
{
void verify(types t)
{
ostringstream log; log << "not suppoted: " << t;
throw std::runtime_error(log.str()); //
}
};
用法:
typedef type_operation<T1_ID, T2_ID, T3_ID, ,,.., TN_ID> type_mapping;
types id;
readFromNetwork(id);
type_mapping::verify(id);
- 值到类型的运行时映射
- 拥有映射的现代方法,该映射可以指向或引用已在堆栈上分配的不同类型的数据
- 如何将高维数据映射到特征类型?
- 如何使映射键具有两种不同的数据类型?
- 值和类型的简洁双向静态 1:1 映射
- 当 map 是一个整数数组并且由 operator[] 创建时,它是否初始化其映射类型
- 编译按映射类型获取元素的映射错误元组
- 模板.参数包扩展 - 重映射类型
- C++函数来返回基于参数的比较器通用映射类型?
- ATL ActiveX DLL 作为映射类型图像和数据加载了两次
- 如何在 C++ 中获取映射类型指针,映射
- 映射类型为的队列容器
- 如何使用boost::mutex作为std::map中的映射类型
- 正向声明映射的映射类型和C++11
- std::map何时调用映射类型的构造函数
- 基于映射类型c++解析和转换csv
- 模板类的映射类型签名(声明)
- c++映射类型的属性
- 使用映射类型会产生GCC错误:预期的未限定id在' for '之前
- 在 EIGEN 中单独声明和初始化映射类型矩阵