类型映射的运行时值 [英] Runtime value to type mapping

查看:38
本文介绍了类型映射的运行时值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以通过网络发送的类型列表,以这个例子为例:

I've got a list of types which can be send over the network, take this example:

enum types {
    E_T1,
    E_T2,
    E_T3,
    E_T4
};

现在我有一个对应于每种类型的类列表,假设每个类都声明为 class E_T1 {...}, class E_T2 {...}

Now I have a list of classes which correspond to each of the types, let's say each is declared as class E_T1 {...}, class E_T2 {...}, etc.

它们不是从公共基类派生的,而且不可能这样做.每个类都有一个验证方法,我需要调用通过网络发送的数据.客户端发送数据 D 和对应于消息类型的 id.我需要获取与类型对应的对象.如果需要,我可以使用 C++0x 特性.

They are not derived from a common base class and it's not possible to do so. Each of the classes has a verification method I need to invoke with the data send over the network. The client sends the data D and a id correspointing to the message type. I need to get hold of the object corresponding to the type. I can use C++0x features if needed.

到目前为止我所尝试的是使用 types 的专用模板,为与之相关的对象保存一个 typedef.这显然是一个愚蠢的想法,因为模板参数需要是编译时常量,所以不可能沿着 getType::type 做一些事情.

What I've tried so far is using specialized templates for the types, holding a typedef for the object related to it. This was obviously a stupid idea as templates parameters need to be compile time constant so doing something along getType<data.id()>::type is not possible.

然后我尝试使用 Boost.Variant 来获得这样的常见可返回类型(使用 mpl 向量在运行时迭代注册类型以进行调试):

Then I tried using Boost.Variant to get a common returnable type like this (used mpl vector to iterate over the registered types at runntime for debbuging):

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 个条目.这可以被覆盖,但根据我的理解,应该考虑每种类型的开销,我们在这里讨论的是几千种类型.

The problem with this is that it's limited to 20 entries per variant per default. This can be overwritten but from what I understood the overhead per type should be considered and we are talking about a few thousand types here.

也试过 boost.any 但它不包含任何类型信息,所以这又是不可能的.有没有人有任何好主意如何优雅地解决这个问题?寻找在我处理类型时不必编写 1k switch 语句的东西.

Also tried boost.any but it doesn't hold any type information so that's out of the question again. Has anyone any good ideas how this can be solved elegantly? Looking for something where I don't have to write a 1k switch statement anytime I handle a type.

所有类型现在都处于编译类型,它们对应的 ID 也是如此.Id -> 类型解析需要在运行时发生.

All types are nown at compile type, same goes for their corresponding IDs. Id -> Type resolving needs to happen at runtime though.

提前致谢,罗宾.

推荐答案

外部多态 (*)

这是一个广为人知的习语,但它被广泛使用:我第一次在 shared_ptr 实现中遇到它,它在我的工具箱中非常有用.

It's a widely known idiom, however it's widely used: I first encountered it in the shared_ptr implementation and it's been quite useful in my toolbox.

这个想法实际上是为所有这些类型创建一个基类.但没有直接从它派生出来.

The idea is to actually create a base class for all those types. But not having them derive from it directly.

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>());
}

因此,这是添加新的间接级别的经典策略.

So, it's the classic strategy of adding a new level of indirection.

现在,您显然确实需要从价值转向类.或者……地图?

Now, you obviously do need a switch to move from value to class. Or perhaps... a map ?

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)();
 }

现在你可以多态地处理它们:

And now you can handle them polymorphically:

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 以便您可以动态注册您的类型(例如插件).

Of course, you're welcome to make the strategy vary according to your needs. For example moving the map out of select so that you can register your types dynamically (like for plugins).

(*) 至少这是我给它起的名字,我很高兴发现它已经被命名了.

(*) At least that's the name I have for it, I would quite happy to find out it's already been named.

这篇关于类型映射的运行时值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆