我有一个可以通过网络发送的类型列表,请举例:
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.Variant来获得这样一个常见的可返回类型(使用mpl vector迭代在runntime上注册的类型进行重新布局):
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开关语句的东西。
所有类型都以编译类型命名,相应的ID也是如此。 Id - &gt;类型解析需要在运行时发生。
提前致谢, 罗宾。
答案 0 :(得分:6)
外部多态性(*)
这是一个广为人知的习语,但它被广泛使用:我第一次在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
,以便您可以动态注册类型(例如插件)。
(*)至少这是我的名字,我很高兴发现它已被命名。
答案 1 :(得分:2)
我假设您有一种处理消息的通用方法,例如重载函数:
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)
来解码和处理消息。
答案 2 :(得分:2)
尝试可变参数模板和您已经定义的getType类:
enum types { T1_ID, T2_ID, .... };
class T1; class T2; class T3; ....
template <types t> struct getType;
template <types t> struct getType<T1_ID> { typedef T1 type; };
template <types t> 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);