2013-02-21 3 views
6

У меня есть интерфейс объекта и открытый набор интерфейсов, которые, возможно, захочет поддерживать.Множественное наследование интерфейсов в C++

// An object 
class IObject 
{ 
    getAttribute() = 0 
} 

// A mutable object 
class IMutable 
{ 
    setAttribute() = 0 
} 

// A lockable object 
class ILockable 
{ 
    lock() = 0 
} 

// A certifiable object 
class ICertifiable 
{ 
    setCertification() = 0 
    getCertification() = 0 
} 

Некоторые производные объекты могут выглядеть следующим образом:

class Object1 : public IObject, public IMutable, public ILockable {} 
class Object2 : public IObject, public ILockable, public ICertifiable {} 
class Object3 : public IObject {} 

Вот мой вопрос: Есть ли способ, чтобы написать функции, которые будут принимать только определенные комбинации этих интерфейсов? Например:

void doSomething(magic_interface_combiner<IObject, IMutable, ILockable> object); 

doSomething(Object1()) // OK, all interfaces are available. 
doSomething(Object2()) // Compilation Failure, missing IMutable. 
doSomething(Object3()) // Compilation Failure, missing IMutable and ILockable. 

Ближайшая вещь, которую я нашел, - boost :: mpl :: inherit. У меня был ограниченный успех, но он не делает именно то, что мне нужно.

Например:

class Object1 : public boost::mpl::inherit<IObject, IMutable, ILockable>::type 
class Object2 : public boost::mpl::inherit<IObject, ILockable, ICertifiable>::type 
class Object3 : public IObject 

void doSomething(boost::mpl::inherit<IObject, ILockable>::type object); 

doSomething(Object1()) // Fails even though Object1 derives from IObject and ILockable. 
doSomething(Object2()) // Fails even though Object2 derives from IObject and ILockable. 

Я думаю, что-то похожее на повышение :: MPL :: унаследуют но это будет генерировать дерево наследования со всеми возможными перестановками поставляемых типов могли бы работать.

Мне также интересны другие подходы к решению этой проблемы. В идеале, что-то, что компилирует проверку времени, в отличие от времени выполнения (т. Е. Нет dynamic_cast).

+2

Вы имеете в виду AND-комбинации или OR-комбинации? –

+1

Исправьте меня, если я ошибаюсь, но разве ваши абстрактные функции не должны быть помечены как 'virtual', а также' = 0'? – ApproachingDarknessFish

+2

Я использую шаблоны вместо этого, это неправильный ответ? –

ответ

1

Может быть, это не самый изящный способ, так как это сделано с C++ 03 синтаксисом

template <typename T, typename TInterface> 
void interface_checker(T& t) 
{ 
    TInterface& tIfClassImplementsInterface = static_cast<TInterface&>(t); 
} 

Это должно дать вам дух трюк. Теперь в вашем случае:

template <typename T, typename TInterface1, typename TInterface2, typename TInterface3 > 
void magic_interface_combiner(T& t) 
{ 
    TInterface1& tIfClassImplementsInterface = static_cast<TInterface1&>(t); 
    TInterface2& tIfClassImplementsInterface = static_cast<TInterface2&>(t); 
    TInterface3& tIfClassImplementsInterface = static_cast<TInterface3&>(t); 
} 

Я предполагаю, что это может быть сделано способом умнее с помощью C++ 11 черт типа.

2

Вы должны использовать static_assert для проверки типов в функции:

#include <type_traits> 

template< typename T > 
void doSomething(const T& t) 
{ 
    static_assert(std::is_base_of<IObject,T>::value, "T does not satisfy IObject"); 
    static_assert(std::is_base_of<IMutable,T>::value, "T does not satisfy IMutable"); 

    // ... 
} 

который даст вам очень хорошие сообщения об ошибках, рассказывающих, какие интерфейсы не удовлетворены. Если вам необходимо перегрузить функцию и иметь версию, которая доступна только для определенной комбинации интерфейса, вы можете также использовать enable_if:

#include <type_traits> 

template< typename T, typename... Is > 
struct HasInterfaces; 

template< typename T > 
struct HasInterfaces<T> : std::true_type {}; 

template< typename T, typename I, typename... Is > 
struct HasInterfaces< T, I, Is... > 
    : std::integral_constant< bool, 
     std::is_base_of< I, T >::value && HasInterfaces< T, Is... >::value > {}; 

template< typename T > 
typename std::enable_if< HasInterfaces< T, IObject, IMutable >::value >::type 
doSomething(const T& t) 
{ 
    // ... 
} 

который сделает функцию исчезает из набора перегрузки при требовании интерфейса не выполняются.

+0

Доступна ли 'static_assert' на языке pre-C++ 11? –

+0

Да: [BOOST_STATIC_ASSERT_MSG (v, msg)] (http://www.boost.org/doc/libs/1_53_0/doc/html/boost_staticassert.html) –

3

Вы можете написать класс проверки интерфейса с использованием рекурсивного VARIADIC наследования:

template<typename... Interfaces> 
struct check_interfaces; 
template<> 
struct check_interfaces<> { 
    template<typename T> check_interfaces(T *) {} 
}; 
template<typename Interface, typename... Interfaces> 
struct check_interfaces<Interface, Interfaces...>: 
public check_interfaces<Interfaces...> { 
    template<typename T> check_interfaces(T *t): 
     check_interfaces<Interfaces...>(t), i(t) {} 
    Interface *i; 
    operator Interface *() const { return i; } 
}; 

Например:

struct IObject { virtual int getAttribute() = 0; }; 
struct IMutable { virtual void setAttribute(int) = 0; }; 
struct ILockable { virtual void lock() = 0; }; 

void f(check_interfaces<IObject, IMutable> o) { 
    static_cast<IObject *>(o)->getAttribute(); 
    static_cast<IMutable *>(o)->setAttribute(99); 
} 

struct MutableObject: IObject, IMutable { 
    int getAttribute() { return 0; } 
    void setAttribute(int) {} 
}; 

struct LockableObject: IObject, ILockable { 
    int getAttribute() { return 0; } 
    void lock() {} 
}; 

int main() { 
    f(new MutableObject); 
    f(new LockableObject); // fails 
} 

Обратите внимание, что check_interfaces имеет след одного указателя на интерфейс проверяемого; это связано с тем, что он выполняет стирание типа в объявленном типе фактического аргумента.

+0

+1 для вариативных шаблонов. –

1

Просто чтобы дать вам небольшой вкус C++ 11:

Single типа без рекурсии:

template <typename... Ts> 
class magic_interface_combiner { 
    typedef std::tuple<Ts*...> Tpl; 
    Tpl tpl; 

    template <typename T, int I> 
    T *as_(std::false_type) 
    { 
    static_assert(I < std::tuple_size<Tpl>::value, "T not found"); 
    return as_<T, I+1>(std::is_same<T, typename std::tuple_element<I+1, Tpl>::type>{}); 
    } 
    template <typename T, int I> 
    T *as_(std::true_type) { return std::get<I>(tpl); } 

public: 
    template <typename T> 
    magic_interface_combiner(T * t) : tpl(static_cast<Ts*>(t)...) {} 

    template <typename T> T * as() { return as_<T, 0>(std::false_type{}); } 
}; 

// no template  
void doSomething(magic_interface_combiner<IObject, IMutable, ILockable> object) 
{ 
} 

Два типа, но без рекурсии:

template <typename T> 
class single_interface_combiner { 
    T *p; 
public: 
    single_interface_combiner(T *t) : p(t) {} 
    operator T*() { return p; } 
}; 

template <typename... Ts> 
struct magic_interface_combiner : single_interface_combiner<Ts>... { 
    template <typename T> 
    magic_interface_combiner(T* t) : single_interface_combiner<Ts>(t)... {} 

    template <typename T> 
    T * as() { return *this; } 
}; 
2

Раствор с помощью std::enable_if и std::is_base_of:

#include <type_traits> 

// An object 
struct IObject 
{ 
    virtual void getAttribute() = 0; 
}; 

// A mutable object 
struct IMutable 
{ 
    virtual void setAttribute() = 0; 
}; 

// A lockable object 
struct ILockable 
{ 
    virtual void lock() = 0; 
}; 

// A certifiable object 
struct ICertifiable 
{ 
    virtual void setCertification() = 0; 
    virtual void getCertification() = 0; 
}; 

struct Object1 : public IObject, public IMutable, public ILockable 
{ 
    void getAttribute() {} 
    void setAttribute() {} 
    void lock() {} 
}; 

struct Object2 : public IObject, public ILockable, public ICertifiable 
{ 
    void getAttribute() {} 
    void lock() {} 
    void setCertification() {} 
    void getCertification() {} 
}; 

struct Object3 : public IObject 
{ 
    void getAttribute() {} 
}; 

template<typename T> 
void doSomething(
    typename std::enable_if< 
     std::is_base_of<IObject, T>::value && 
     std::is_base_of<IMutable, T>::value && 
     std::is_base_of<ILockable, T>::value, 
     T>::type& obj) 
{ 
} 

int main() 
{ 
    Object1 object1; 
    Object2 object2; 
    Object3 object3; 

    doSomething<Object1>(object1); // Works 
    doSomething<Object2>(object2); // Compilation error 
    doSomething<Object3>(object3); // Compilation error 
} 
Смежные вопросы