2010-01-18 1 views
5

में संकलन-समय इंटरफ़ेस कार्यान्वयन जांच I C++ में छद्म-इंटरफेस का उपयोग कर रहा हूं, यानी शुद्ध सार वर्ग। मान लीजिए मेरे पास तीन इंटरफेस, इफू, इबार और आईक्यूक्स हैं। एक फ्रेड काम करेगा, उदाहरण के लिए -सी ++

interface IFoo 
{ 
    void foo (void); 
} 

interface IBar 
{ 
    void bar (void); 
} 

interface IQuux 
{ 
    void quux (void); 
} 

class Fred : implements IFoo, IBar, IQuux 
{ 
} 

मैं एक विधि है कि किसी भी वस्तु है कि IFoo और Ibar लागू करता है स्वीकार करता है की घोषणा करना चाहते हैं: मैं भी एक वर्ग फ्रेड है कि उनमें से सभी तीन को लागू करता है। एक IFooAndBar * प्राप्त करने के रूप

interface IFooAndBar : extends IFoo, IBar 
{ 
} 

class Fred : implements IFooAndBar, IQuux 
{ 
} 

अब मैं अपने प्रणाली की घोषणा कर सकते हैं: केवल संकलन समय जिस तरह से यह मैं कल्पना कर सकते हैं करने के लिए एक तिहाई इंटरफ़ेस IFooAndBar है कि दोनों को लागू करता है परिभाषित करने के लिए, और फ्रेड redeclare है। अब तक सब ठीक है।


हालांकि, क्या होता है अगर मैं भी एक अलग तरीके है कि Ibar और IQuux स्वीकार करता है करना चाहते हैं? मैं एक नया इंटरफेस IBarAndQuux घोषित करने और दोनों इनहेरिट के रूप में फ्रेड घोषित करने की कोशिश की:

class IFooAndBar : IFoo, IBar 
{ 
}; 


class IBarAndQuux : IBar, IQuux 
{ 
}; 


class Fred : IFooAndBar, IBarAndQuux 
{ 
}; 

यह काम करता है जब मैं एक पद्धति के लिए एक IFooAndBar के रूप में फ्रेड पास; हालांकि, जब मैं फ्रेड :: बार फोन करने की कोशिश() सीधे, जीसीसी की शिकायत:

error: request for member ‘bar’ is ambiguous 
error: candidates are: void IBar::bar() 
error:     void IBar::bar() 

जो इस समाधान कम या ज्यादा बेकार बना देता है।


मेरा अगला प्रयास तीन अलग-अलग इंटरफेस से इनहेरिट, और विधि एक पैरामीटर के रूप संकर इंटरफेस में से एक को स्वीकार कर रही है के रूप में फ्रेड घोषित करने के लिए किया गया था:

class Fred : public IFoo, public IBar, public IBaz 
{ 

}; 

void doTest (IBarAndBaz* pObj) 
{ 
    pObj->bar(); 
    pObj->baz(); 
} 

जब मैं पारित करने के लिए कोशिश

error: cannot convert ‘Fred*’ to ‘IBarAndBaz*’ for argument ‘1’ to ‘void doTest(IBarAndBaz*)’ 

dynamic_cast < p> भी: IBarAndBaz के रूप में फ्रेड * पैरामीटर, मैं एक त्रुटि, अपेक्षा के अनुरूप मिल एक

error: cannot dynamic_cast ‘pFred’ (of type ‘class Fred*’) to type ‘class IBarAndBaz*’ (source type is not polymorphic) 

एक डाली काम है, तथापि करता जबरदस्ती त्रुटि (जो मुझे समझ नहीं आता) roduces:

doTest((IBarAndBaz*)pFred); 

लेकिन मुझे आश्चर्य है कि सुरक्षित और पोर्टेबल यह है (मैं लिनक्स के लिए विकसित कैसे, मैक और विंडोज), और क्या यह वास्तविक दुनिया की स्थिति में काम करता है।


अंत में, मुझे लगता है मेरी विधि अन्य (रों) कार्यावधि में सही पैरामीटर प्रकार लागू करने के लिए करने के लिए इंटरफेस में से एक और dynamic_cast के लिए सूचक स्वीकार कर सकते हैं, लेकिन मैं एक संकलन समय समाधान पसंद करते हैं।

+2

यह सुरक्षित नहीं है और यह पोर्टेबल है। वास्तव में, यह सी ++ में अपरिभाषित है। –

उत्तर

6

पहले परीक्षण किया समाधान का उपयोग करने पर विचार करें - बचाव के लिए Boost.TypeTraits:

tempate<class C> 
void doTest(C* pObj) 
{ 
    pObj->bar(); 
    pObj->baz(); 
} 

वर्गों है कि बार() और baz की आपूर्ति के लिए सही ढंग से व्यवहार करेंगे:

template<class T> 
void takeFooAndBar(const T& t) { 
    BOOST_STATIC_ASSERT(
      boost::is_base_of<IFoo, T>::value 
     && boost::is_base_of<IBar, T>::value); 
    /* ... */ 
} 
+1

बूस्ट के बाहर एक दुनिया है :-) – mmmmmmmm

+0

बेशक, बीसीपी उसमें मदद करता है;) लेकिन गंभीरता से, कंपाइलर-वर्कअराउंड्स के इतने व्यापक संग्रह को डुप्लिकेट क्यों करें, खासतौर से टाइप गुणों और इसी तरह के लिए? –

+1

मुझे नहीं लगता कि सामान्य सी ++ (वर्चुअल विरासत) समस्या को हल कर सकता है तो बड़ी बूस्ट बंदूक प्राप्त करना एक अच्छा विचार है। – mmmmmmmm

1

आप टेम्पलेट metaprogramming का उपयोग कर प्रभाव प्राप्त कर सकते हैं(), और किसी अन्य वर्ग के लिए संकलन करने में विफल।

+0

यह बहुत दिलचस्प है। मेरा वास्तविक जीवन मामला अधिक जटिल है, असल में - मेरे पास एक कक्षा है जो ऑब्जेक्ट प्राप्त करती है जिसे इंटरफेस को कार्यान्वित करने की आवश्यकता होती है, इसमें पॉइंटर रखती है, और फिर इसकी विधियों को कॉल करती है। मैं उस बड़े वर्ग को टेम्पलेट के रूप में कार्यान्वित करने में सहज नहीं हूं :( – ggambett

+0

एक संकलन-समय का दावा करना संभव है कि एक प्रकार उस कार्य को आमंत्रित किए बिना एक विशेष सदस्य फ़ंक्शन प्रदान करता है। आप ऐसा टेम्पलेट वाले रैपर में ऐसा कर सकते हैं जो तब आमंत्रित करता है वास्तविक कार्य, टेम्पलेट कोड को इनलाइन विशेषता के साथ घोषित किया जा रहा है, इसलिए यह संकलित हो जाता है। हालांकि, कार्यान्वयन बालों वाली है - इसे अपने आप को रोल करने से पहले (जीएफ के उत्तर देखें) का उपयोग करना आसान है। – moonshadow

3

OO शैली में ऐसा करने के लिए, आप आभासी विरासत की जरूरत है सुनिश्चित करें कि Fred केवल IBar की एक प्रति के साथ समाप्त होता है:

class IFooAndBar : public IFoo, public virtual IBar {}; 
class IBarAndQuux : public virtual IBar, public IQuux {}; 

class Fred : public IFooAndBar, public IBarAndQuux {}; 

Fred fred; 
fred.bar(); // unambiguous due to virtual inheritence 

के रूप में अन्य लोगों ने कहा है, तो आप का उपयोग कर अपने दूसरे प्रयास के लिए इसी तरह कुछ कर सकते हैं स्थिर polymorphism पाने के लिए टेम्पलेट्स।

जिस कास्ट आप कोशिश कर रहे थे वह संभव नहीं है, Fred का उदाहरण IBarAndBaz का उदाहरण नहीं है। मजबूर कास्ट संकलित करता है क्योंकि अधिकतर मजबूर कास्ट संकलित होगा, चाहे रूपांतरण सुरक्षित है या नहीं, लेकिन इस मामले में यह अपरिभाषित व्यवहार देगा।

संपादित: वैकल्पिक रूप से, आप टेम्पलेट का उपयोग नहीं करना चाहते हैं और इंटरफेस के सभी संभव समूहों को परिभाषित करने के combinatorical विस्फोट पसंद नहीं है, तो आप एक अलग कार्यों पैरामीटर के रूप में प्रत्येक इंटरफ़ेस लेने के लिए निर्धारित कर सकते हैं:

void doTest(IBar *bar, IBaz *baz) 
{ 
    bar->bar(); 
    baz->baz(); 
} 

class Fred : public IBar, public IBaz {}; 

Fred fred; 
doTest(&fred,&fred); 
+0

दुर्भाग्य से यह काम कर सकता है। कि मुझे फ्रेड की परिभाषा में इंटरफ़ेस (कम से कम मैं उपयोग करने वाले) के सभी संभावित संयोजनों को घोषित करना होगा ... यदि यह उपयोगकर्ता विधि स्थान पर हल किया जा सकता है, तो पैरामीटर स्थान पर नहीं, यह बेहतर होगा टेम्पलेट आधारित समाधान करते हैं। – ggambett

1

आप क्या कर सकते एक टेम्प्लेटेड निर्माता है कि एक मनमाना सूचक को स्वीकार करता है के साथ एक वर्ग बनाने है, दो इंटरफेस आप चाहते हैं पाने के लिए निहित downcasting का उपयोग करता है, और उसके बाद संयुक्त इंटरफ़ेस लागू करता है।

struct IFoo 
{ 
    virtual void foo() = 0; 
}; 

struct IBar 
{ 
    virtual void bar() = 0; 
}; 

struct IFooAndBar : public IFoo, public IBar {}; 

class FooAndBarCompositor : public IFooAndBar 
{ 
public: 
    template <class T> 
    FooAndBarCompositor(T* pImpl) : m_pFoo(pImpl), m_pBar(pImpl) {} 

    void foo() {m_pFoo->foo();} 
    void bar() {m_pBar->bar();} 

private: 
    IFoo* m_pFoo; 
    IBar* m_pBar; 
}; 

तो फिर तुम एक समारोह है कि IFooAndBar * स्वीकार करता है दोनों इंटरफेस के लिए आवश्यक हैं लिखते हैं, और फोन करने वाले ढेर है कि अपनी पसंद की वस्तु को डिस्पैच पर एक FooAndBarCompositor निर्माण कर सकते हैं। ऐसा लगता है:

void testFooAndBar(IFooAndBar* pI) {} 

void baz(Fred* pFred) 
{ 
    FooAndBarCompositor fb(pFred); 
    testFooAndBar(&fb); 
} 

यह बहुत सामान्य नहीं है, और आपको कंपोजिटर में प्रेषण कार्यों को लिखने के लिए मजबूर करता है। एक और दृष्टिकोण एक सामान्य इंटरफ़ेस कंपोज़िटर टेम्पलेट है:

template <class IA, class IB> 
class InterfaceCompositor 
{ 
public: 
    template <class T> 
    InterfaceCompositor(T* pObj) : m_pIA(pObj), m_pIB(pObj) {} 

    IA* AsA() const {return m_pIA;} 
    operator IA*() const {return AsA();} 
    IB* AsB() cosnt {return m_pIB;} 
    operator IB*() const {return AsB();} 

private: 
    IA* m_pIA; 
    IB* m_pIB; 
}; 

तब समारोह लगता है:

void testFooAndBar(InterfaceCompositor<IFoo, IBar> pI) 
{ 
    IFoo* pFoo = pI; // Or pI.AsA(); 
    IBar* pBar = pI; // Of pI.AsB(); 
} 

इस समारोह कई इंटरफेस को लागू करने के लिए या तो कंपोज़िटर का उपयोग जहां एक एक करना चाहता है कि आवश्यकता है * या बी * अपेक्षित है (उदाहरण के लिए असाइनमेंट या फ़ंक्शन पैरामीटर) या स्पष्ट रूप से उपयुक्त AsX() विधि को कॉल करें। विशेष रूप से, उपयोग करने के लिए इंटरफ़ेस -> ऑपरेटर के उपयोग से अनुमानित नहीं किया जा सकता है और * ऑपरेटर का समग्र पर कोई अर्थ नहीं है।

यदि आप जेनेरिक कोड के साथ जाते हैं, तो आप यह लागू करने के लिए उसी टेम्पलेट का उपयोग कर सकते हैं कि ऑब्जेक्ट आईबीर और आईबाज़ दोनों का भी समर्थन करता है।

सी ++ 0x वैरैडिक टेम्पलेट्स पेश करेगा जो इस अवधारणा को इंटरफ़ेस कक्षाओं की मनमानी संख्याओं तक विस्तारित करने की अनुमति देगा।