2012-08-13 12 views
5

एक गैर-बूस्ट प्रोजेक्ट में, मेरे पास एक कक्षा है जो एक निश्चित उपयोगकर्ता कार्रवाई (बटन दबाया/जारी) के आधार पर टाइमर का उपयोग करती है। मैं इस वर्ग को सामान्य बनाना चाहता हूं, इसलिए उपयोगकर्ता परिभाषित कार्यों के लिए कॉलबैक लेता है।असंबद्ध वर्गों के बीच सी ++ कॉलबैक कैसे पास करें?

// TimerClass.h 
typedef void (*timerCallback)(void); 
... 
Class TimerClass : public CButton { 
public: 
    void setCallbackShortTime(timerCallback clickFn) { shortCallback = clickFn;} ; 
    void setCallbackLongTime(timerCallback clickFn) { longCallback = clickFn;} ; 
private: 
    timerCallback shortCallback, longCallback; 
} 


// CMyDlg.h 
class CMyDlg : public CDialog 
{ 
public: 
    void openLiveViewWindow(); 
    void openAdminPanelWindow(); 
    TimerClass _buttonSettings; 
} 

// CMyDlg.cpp 
... 
_buttonSettings.setCallbackShortTime(&openLiveViewWindow); 
... 

अब, एक और वर्ग (DialogClass) से मैं TimerClass उपयोग कर सकते हैं, लेकिन मैं कॉलबैक कार्यों के लिए समारोह संकेत पारित नहीं कर सकते हैं। ये कार्य स्थिर नहीं हैं। संकलक शिकायत समाप्त होता है:

error C2276: '&' : illegal operation on bound member function expression 

कुछ शोध इस पर std::function() और std::bind() को बताया, लेकिन मैं इन से परिचित नहीं हूँ और यह कैसे हल करने पर कुछ संकेत की सराहना करेंगे।

संकल्प: किसी को दिलचस्पी के लिए, यहाँ अंतिम समाधान

// TimedButton.h 
#include <functional> 
// define timer callback prototype 
typedef std::function<void()> timerCallback; 
... 
class TimedButton : public CButton 
{ 
public: 
    TimedButton(); 
    ... 
    void setCallbackShortTime(timerCallback clickFn) { _shortCallback = clickFn;} ; 
    void setCallbackLongTime(timerCallback clickFn) { _longCallback = clickFn;} ; 
private: 
    timerCallback _shortCallback; 
    timerCallback _longCallback; 
} 

// TimedButton.cpp 
... 
(_longCallback)(); // call long duration fn 
... 
(_shortCallback)();  // call short duration fn 

// in MyDlg.cpp 
#include <functional> 
... 
_buttonSettings.setCallbackShortTime(std::bind(&CMyDlg::openLiveViewWindow, this)); 
_buttonSettings.setCallbackLongTime(std::bind(&CMyDlg::openAdminPanelWindow, this)); 
+0

सदस्य कार्यों के लिए कॉलबैक अंतर्निहित 'इस' पैरामीटर के कारण अलग-अलग व्यवहार करते हैं। –

+0

विधियों को स्थिर किए बिना, आपको टाइमर के भीतर उदाहरण का संदर्भ रखना होगा। क्या आप सी ++ 11 का उपयोग कर रहे हैं? – jli

+0

हम सी ++ 11 का उपयोग नहीं कर रहे हैं, लेकिन पिछले एक के रूप में हमारे पास 'tr1' फ़ंक्शन – fduff

उत्तर

2

std::function की ईंटों हैं एक बहुरूपी समारोह वस्तु है, जो एक विशेष हस्ताक्षर के साथ प्रतिदेय वस्तु किसी भी प्रकार की लपेट कर सकते हैं। आपके मामले में, आप यह कोई तर्क ले और कोई मूल्य नहीं लौटना चाहते हैं, ताकि आप को परिभाषित कर सकते हैं:

typedef std::function<void()> timerCallback; 

std::bind यदि आप कोई दूसरा हस्ताक्षर से एक के लिए एक प्रतिदेय वस्तु अनुकूल करने के लिए मापदंडों के तर्क जुड़ कर सकते हैं। आपके मामले में, आप एक विशेष वस्तु के लिए यह बाध्यकारी उस पर आह्वान करने के लिए द्वारा एक सदस्य समारोह अनुकूल करने के लिए करना चाहते हैं:

_buttonSettings.setCallbackShortTime(std::bind(&CMyDlg::openLiveViewWindow, this)); 

ध्यान दें कि इन 2011 में शुरू किए गए थे, इसलिए पुराने compilers उन्हें समर्थन नहीं करेंगे। उस स्थिति में, आप या तो बहुत ही बूस्ट या टीआर 1 पुस्तकालयों का उपयोग कर सकते हैं, या अपनी खुद की कॉल करने योग्य कक्षा बना सकते हैं जिसमें पॉइंटर-टू-सदस्य-फ़ंक्शन और ऑब्जेक्ट का एक पॉइंटर/संदर्भ होता है जिसे आप इसे आमंत्रित करना चाहते हैं।

+0

मुझे सही दिशा में इंगित करने के लिए माइक चीयर्स। – fduff

2

आप कक्षा के एक विधि, केवल सादा कार्यों के लिए एक सूचक नहीं पारित कर सकते हैं। मैं std::function() में खुदाई करने का सुझाव देता हूं, क्योंकि आप वीएस -2010 का उपयोग कर रहे हैं, जो उनका समर्थन करता है। उनके और अधिक here का वर्णन करने वाला एक अच्छा (और लंबा) ट्यूटोरियल है।

+1

एक छोटी स्पष्टीकरण: यहां समस्या यह है कि 'टाइमर कॉलबैक' को सामान्य फ़ंक्शन पर पॉइंटर के रूप में परिभाषित किया जाता है, ताकि आप उसमें सदस्य फ़ंक्शन में पॉइंटर स्टोर न कर सकें। यदि इसे सदस्य फ़ंक्शन के सूचक के रूप में परिभाषित किया गया था, तो आप कर सकते थे। लेकिन फिर आपको इसे कॉल करने के लिए कहीं भी 'यह' पॉइंटर होना होगा। यही कारण है कि std :: बाइंड सही जवाब है, जैसा कि अन्य ने कहा है। –

+0

हाँ, स्पष्टीकरण के लिए धन्यवाद। मुझे पहले स्थान पर प्रदान करना चाहिए था ... :) –

+0

लिंक के लिए धन्यवाद, वास्तव में बहुत दिलचस्प है। – fduff

3

ऐसा करने का पुराना तरीका है कि आपका कॉलबैक फ़ंक्शन अतिरिक्त void* पैरामीटर स्वीकार कर ले, जिसके लिए आप उस ऑब्जेक्ट को पॉइंटर पास करते हैं जिस पर आप फ़ंक्शन को कॉल करना चाहते हैं। फिर आप कॉलबैक के लिए एक स्थिर सदस्य फ़ंक्शन का उपयोग करते हैं और इसे पॉइंटर को उचित ऑब्जेक्ट में डालने दें और अपना सच्चा कॉलबैक कॉल करें।

typedef void (*timerCallback)(void*); 
... 
void setCallbackShortTime(timerCallback clickFn, void* object) { shortCallback = clickFn; shortCallbackObject = object;} ; 
void setCallbackLongTime(timerCallback clickFn, void* object) { longCallback = clickFn; longCallbackObject = object;} ; 
... 

static void openLiveViewWindowCallback(void* object) { ((CMyDlg*)object)->openLiveViewWindow(); } 
void openLiveViewWindow(); 
+0

क्या यह पुराना फैशन है? मैंने इसे अक्सर इस्तेमाल किया है! – acraig5075

+0

जो इसे करने के लिए बहुत सी तरह का तरीका है। एकमात्र समस्या यह है कि कॉलबैक को ऑब्जेक्ट प्रकार '((सीएमडीडीएलजी *) ऑब्जेक्ट) -> 'जो सामान्य वर्ग के उद्देश्य के खिलाफ है, को जानने की आवश्यकता है। – fduff

1

आप एक पॉलीमोर्फिक टेम्पलेट क्लास बना सकते हैं जो फ़ंक्शन पॉइंटर के रूप में कार्य करता है।

class TFunctorBase 
{ 
public: 
    TFunctorBase(void) {} 
    virtual ~TFunctorBase(void) {} 
    virtual void operator()(void) = 0; 
}; 

// derived template class 
template <class TClass> class TFunctor : public TFunctorBase 
{ 
private: 
    void (TClass::*fpt)(); // pointer to member function 
    TClass* pt2Object;     // pointer to object 

public: 
    TFunctor(TClass* _pt2Object, void(TClass::*_fpt)()) 
    { pt2Object = _pt2Object; fpt = _fpt;}; 

    virtual void operator()(void) 
    { (*pt2Object.*fpt)();};    // execute member function 
}; 

एक functor वस्तु को प्रारंभ करने के लिए:

TFunctor<MyClass> obj(&myInstance, &MyClass::myMemberFunction); 

और यह उपयोग करने के लिए:

class ClassA 
{ 
public: 
    void function1(int a, string b); 
}; 

ClassA objA; 
TFunctor<ClassA> functor(&objA,&ClassA::function); 
(*functor)(42, "pumpkin"); //assuming you added virtual void operator()(int, string) to TFunctorBase 

यहाँ functors के बारे में एक महान संसाधन के साथ है:

(*obj)(); 
//(*obj)(42); for operator()(int) 

यहाँ एक उदाहरण है मेरे पास कार्यान्वयन के साथ है ऊपर वर्णित। http://www.newty.de/fpt/functor.html#chapter4

+0

या आप 'std :: bind' का उपयोग कर सकते हैं, जिसमें कई फायदे हैं: यह अधिक शक्तिशाली है, यह मानक लाइब्रेरी में है (सी ++ 11 से शुरू होता है) इसलिए आपके कोड के भविष्य के रखरखाव और अधिक पोर्टेबल और किसी और को पहचानने योग्य और इसे बनाए रखने के लिए जिम्मेदार है। –

संबंधित मुद्दे