我正在使用一个简单的对象模型,其中对象可以实现接口以提供可选功能。本质上,一个对象必须实现 getInterface 给出了(唯一)接口ID的方法。然后,如果对象未实现请求的接口,则该方法将返回接口或空的指针。这是一个代码草图,可以说明以下内容:

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 模板,或通过建议将对象/接口框架易于使用的替代方案。

有帮助吗?

解决方案

那这样的事情呢?

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() 当您查询时。

其他提示

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的查询方面是否为此 - 他们使用编译器扩展程序的类似设计。我从未见过实现的COM代码,只使用了标题,但是您可以搜索它。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top