2013-02-21 103 views
6

में इंटरफेस की एकाधिक विरासत मेरे पास एक ऑब्जेक्ट इंटरफ़ेस और इंटरफेस का एक खुला अंत संग्रह है जो व्युत्पन्न ऑब्जेक्ट का समर्थन करना चाह सकता है।सी ++

// 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 :: वारिस है। मुझे कुछ सीमित सफलता मिली है, लेकिन यह वही नहीं करता जो मुझे चाहिए।

उदाहरण के लिए:

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. 

मैं :: एमपीएल :: इनहेरिट बढ़ावा देने के लिए कुछ इसी तरह लगता है लेकिन यह है कि आपूर्ति की प्रकार के सभी संभव क्रमपरिवर्तन काम हो सकता है के साथ एक विरासत पेड़ उत्पन्न होगा।

मैं इस समस्या को हल करने के लिए अन्य दृष्टिकोणों के बारे में भी उत्सुक हूं। आदर्श रूप से ऐसा कुछ जो रनटाइम के विपरीत समय जांच करता है (यानी कोई गतिशील_कास्ट नहीं)।

+2

क्या आपका मतलब एंड-संयोजन, या OR-संयोजन है? –

+1

यदि मैं गलत हूं, तो मुझे सही करें, लेकिन क्या आपके अमूर्त कार्यों को 'आभासी' के साथ-साथ '= 0' चिह्नित करने की आवश्यकता नहीं है? – ApproachingDarknessFish

+2

मैं इसे टेम्पलेट का उपयोग करता हूं बल्कि गलत जवाब है? –

उत्तर

1

शायद यह नहीं सबसे खूबसूरत तरीका है, क्योंकि यह सी ++ 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); 
} 

मुझे लगता है कि जिस तरह से होशियार सी ++ 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' प्री-सी ++ 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

बस आप की सी ++ 11 एक छोटा सा स्वाद देने के लिए

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 
}