【问题标题】:What are alternatives to this typelist-based class hierarchy generation code?这种基于类型列表的类层次结构生成代码有哪些替代方法?
【发布时间】:2011-06-11 18:04:18
【问题描述】:

我正在使用一个简单的对象模型,其中对象可以实现接口以提供可选功能。从本质上讲,一个对象必须实现一个getInterface 方法,该方法被赋予一个(唯一的)接口ID。然后,该方法返回一个指向接口的指针 - 或 null,以防对象未实现请求的接口。这是一个代码草图来说明这一点:

struct Interface { };
struct FooInterface : public Interface { enum { Id = 1 }; virtual void doFoo() = 0; };
struct BarInterface : public Interface { enum { Id = 2 }; virtual void doBar() = 0; };
struct YoyoInterface : public Interface { enum { Id = 3 }; virtual void doYoyo() = 0; };

struct Object {
    virtual Interface *getInterface( int id ) { return 0; }
};

为了让在这个框架中工作的客户更轻松,我使用了一个小模板,它会自动生成“getInterface”实现,这样客户只需实现接口所需的实际功能。这个想法是从Object 以及所有接口派生一个具体类型,然后让getInterface 只返回指向this 的指针(转换为正确的类型)。这是模板和演示用法:

struct NullType { };
template <class T, class U>
struct TypeList {
    typedef T Head;
    typedef U Tail;
};

template <class Base, class IfaceList>
class ObjectWithIface :
    public ObjectWithIface<Base, typename IfaceList::Tail>,
    public IfaceList::Head
{
public:
    virtual Interface *getInterface( int id ) {
        if ( id == IfaceList::Head::Id ) {
            return static_cast<IfaceList::Head *>( this );
        }
        return ObjectWithIface<Base, IfaceList::Tail>::getInterface( id );
    }
};

template <class Base>
class ObjectWithIface<Base, NullType> : public Base
{
public:
    virtual Interface *getInterface( int id ) {
        return Base::getInterface( id );
    }
};

class MyObjectWithFooAndBar : public ObjectWithIface< Object, TypeList<FooInterface, TypeList<BarInterface, NullType> > >
{
public:
    // We get the getInterface() implementation for free from ObjectWithIface
    virtual void doFoo() { }
    virtual void doBar() { }
};

这很好用,但是有两个难看的问题:

  1. 对我来说,一个阻碍是这不适用于 MSVC6(它对模板的支持很差,但不幸的是我需要支持它)。 MSVC6 在编译时会产生 C1202 错误。

  2. 由递归ObjectWithIface 模板生成整个类范围(线性层次结构)。这对我本身来说不是问题,但不幸的是我不能只做一个switch 语句来将接口ID 映射到getInterface 中的指针。相反,层次结构中的每个步骤都会检查单个接口,然后将请求转发到基类。

有人对如何改善这种情况提出建议吗?通过使用ObjectWithIface 模板修复上述两个问题,或者通过建议使对象/接口框架更易于使用的替代方案。

【问题讨论】:

  • 如果您需要支持 VC6,我认为您在模板元编程技巧方面的选择是有限的。
  • @jalf:我肯定是有限的,是的 - 我仍然希望在这些限制内有所改进。 :-]

标签: c++ templates metaprogramming typelist


【解决方案1】:

dynamic_cast 存在于解决这个确切问题的语言中。

示例用法:

class Interface { 
    virtual ~Interface() {} 
}; // Must have at least one virtual function
class X : public Interface {};
class Y : public Interface {};

void func(Interface* ptr) {
    if (Y* yptr = dynamic_cast<Y*>(ptr)) {
        // Returns a valid Y* if ptr is a Y, null otherwise
    }
    if (X* xptr = dynamic_cast<X*>(ptr)) {
        // same for X
    }
}

dynamic_cast 还可以无缝处理多重继承和虚拟继承等问题,您可能会遇到这些问题。

编辑:

您可以检查 COM 的 QueryInterface - 他们使用带有编译器扩展的类似设计。没见过COM代码实现,只用到了headers,你可以搜索一下。

【讨论】:

  • dynamic_cast 不能跨 DLL 边界工作,这对我来说是一个交易破坏者。这就是这个自制 RTTI 的全部“存在理由”。此外,我的问题中的接口系统允许在 runtime 返回不同的接口。 dynamic_cast 基于 C++ 类型,是静态的。
  • @Frerich:没有提到你在 OP 中需要那个。
【解决方案2】:

类似的东西呢?

struct Interface
{
    virtual ~Interface() {}
    virtual std::type_info const& type() = 0;
};

template <typename T>
class InterfaceImplementer : public virtual Interface 
{
    std::type_info const& type() { return typeid(T); }
};

struct FooInterface : InterfaceImplementer<FooInterface>
{
    virtual void foo();
};

struct BarInterface : InterfaceImplementer<BarInterface>
{
    virtual void bar();
};

struct InterfaceNotFound : std::exception {};

struct Object
{
    void addInterface(Interface *i)
    {
        // Add error handling if interface exists
        interfaces.insert(&i->type(), i);
    }

    template <typename I>
    I* queryInterface()
    {
        typedef std::map<std::type_info const*, Interface*>::iterator Iter;
        Iter i = interfaces.find(&typeid(I));
        if (i == interfaces.end())
            throw InterfaceNotFound();

        else return static_cast<I*>(i->second);
    }

private:
    std::map<std::type_info const*, Interface*> interfaces;
};

如果您想跨动态库边界执行此操作,您可能需要比type_info const* 更精细的东西。像std::stringtype_info::name() 这样的东西可以正常工作(虽然有点慢,但这种极端的调度可能需要一些缓慢的东西)。您也可以制造数字 ID,但这可能更难维护。

存储 type_infos 的哈希值是另一种选择:

template <typename T>
struct InterfaceImplementer<T>
{
    std::string const& type(); // This returns a unique hash
    static std::string hash(); // This memoizes a unique hash
};

添加接口时使用FooInterface::hash(),查询时使用虚拟Interface::type()

【讨论】:

    猜你喜欢
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多