2011-01-19 1 views
3

개체가 선택적 기능을 제공하기 위해 인터페이스를 구현할 수있는 간단한 개체 모델로 작업하고 있습니다. 핵심은 객체가 (고유 한) 인터페이스 ID가 부여 된 getInterface 메소드를 구현해야한다는 것입니다. 그런 다음 메서드는 인터페이스에 대한 포인터를 반환하거나 객체가 요청 된 인터페이스를 구현하지 못하는 경우 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 템플릿에 의해 모든 범위의 클래스 (선형 계층 구조)가 생성됩니다. 이것은 본인에게 문제가 아니지만 유감스럽게도 인터페이스 ID를 getInterface의 포인터에 매핑하기 위해 단일 문을 사용할 수는 없습니다. 대신 계층 구조의 각 단계는 단일 인터페이스를 확인한 다음 요청을 기본 클래스로 전달합니다.

이 상황을 개선하는 방법에 대한 의견이 있으십니까? 위의 두 가지 문제를 ObjectWithIface 템플릿으로 수정하거나 Object/Interface 프레임 워크를보다 쉽게 ​​사용할 수있는 대안을 제안하십시오.

+0

VC6을 지원해야하는 경우 템플리트 메타 프로그래밍 측면에서 귀하의 옵션이 제한적이라고 생각합니다. – jalf

+0

@ jalf : 저는 분명히 제한되어 있습니다, 그렇습니다 - 나는이 한계 내에서 개선을 기대하고있었습니다. : -] –

답변

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도 제조 할 수 있지만 유지 보수가 더 어려울 수 있습니다.

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() 당신 조회 : type_infos의 해시를 저장

다른 옵션입니다.

3

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도 완벽하게 당신이 잘 투쟁 수있는 다중 가상 상속 등의 일을 처리 할 것입니다.

편집 :

당신은 그들이 컴파일러 확장과 유사한 디자인을 사용 this- 위해 COM의 대한 QueryInterface를 확인할 수 있습니다. 필자는 COM 코드가 구현 된 것을 본 적이 없으며 헤더 만 사용했지만 검색 할 수는 있습니다.

+0

'dynamic_cast'는 DLL 경계를 넘어서서 작동하지 않습니다. 이것은 나를위한 거래 차단기입니다. 그것은이 자체 제작 RTTI를위한 전체 "존재 이유"입니다. 또한, 내 질문에 인터페이스 시스템은 * 런타임 *에서 다른 인터페이스를 반환 할 수 있습니다. 'dynamic_cast'는 정적 인 C++ 타입을 기반으로합니다. –

+0

@Frerich : OP에서 필요하다고 언급하지 않았습니다. – Puppy

관련 문제