कक्षा सदस्य का उपयोग करके C ++ कॉलबैक


89

मुझे पता है कि यह कई बार पूछा गया है, और इसकी वजह से क्रॉफ्ट के माध्यम से खुदाई करना और क्या काम करता है इसका एक सरल उदाहरण मिल सकता है।

मुझे यह मिल गया है, यह सरल है और यह काम करता है MyClass...

#include <iostream>
using std::cout;
using std::endl;

class MyClass
{
    public:
        MyClass();
        static void Callback(MyClass* instance, int x);
    private:
        int private_x;
};

class EventHandler
{
    public:
        void addHandler(MyClass* owner)
        {
            cout << "Handler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner,1);
        }
};

EventHandler* handler;

MyClass::MyClass()
{
    private_x = 5;
    handler->addHandler(this);
}

void MyClass::Callback(MyClass* instance, int x)
{
    cout << x + instance->private_x << endl;
}

int main(int argc, char** argv)
{
    handler = new EventHandler();
    MyClass* myClass = new MyClass();
}

class YourClass
{
    public:
        YourClass();
        static void Callback(YourClass* instance, int x);
};

कैसे कि इतने में लिखा जा सकता EventHandler::addHandler()दोनों के साथ काम करेंगे MyClassऔर YourClass। मुझे क्षमा करें, लेकिन यह मेरे मस्तिष्क के काम करने का तरीका है, मुझे यह समझने का एक सरल उदाहरण देखने की आवश्यकता है कि इससे पहले कि मैं काम करता हूं / यह कैसे काम करता है। यदि आपको इस काम को करने का पसंदीदा तरीका मिल गया है तो इसे दिखाने का समय है, कृपया उस कोड को चिह्नित करें और उसे वापस पोस्ट करें।

[संपादित करें]

इसका उत्तर दिया गया था लेकिन चेकमार्क देने से पहले उत्तर को हटा दिया गया था। मेरे मामले में जवाब एक अस्थायी कार्य था। इसमे एडहैंडलर को बदला गया ...

class EventHandler
{
    public:
        template<typename T>
        void addHandler(T* owner)
        {
            cout << "Handler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner,1);
        }
};

4
टेम्पर्ड फंक्शन का उदाहरण किसने पोस्ट किया? आपको चेकमार्क मिल गया, लेकिन मैंने परीक्षण करते समय आपने अपना उत्तर हटा दिया। यह वही हुआ जो मुझे चाहिए था। एक साधारण फ़ंक्शन टेम्प्लेट मुझे पढ़ने वाली अन्य सभी जानकारी के स्टू में खो गया। आपका उत्तर प्रश्न के रूप में संपादित किया गया।
बेंटफ़एक्स

मुझे लगता है कि यह JaredC था। आपको उसका शिकार करने की आवश्यकता हो सकती है = P
WhozCraig

जवाबों:


182

इसके बजाय स्थिर तरीकों होने और वर्ग उदाहरण के लिए सूचक के आसपास पास करने का, आप नए सी ++ 11 मानक में कार्यक्षमता का उपयोग कर सकते हैं: std::functionऔर std::bind:

#include <functional>
class EventHandler
{
    public:
        void addHandler(std::function<void(int)> callback)
        {
            cout << "Handler added..." << endl;
            // Let's pretend an event just occured
            callback(1);
        }
};

addHandlerविधि अब एक स्वीकार करता है std::functionतर्क है, और इस "समारोह वस्तु" कोई वापसी मान है और तर्क के रूप में एक पूर्णांक लेता है।

इसे किसी विशिष्ट फ़ंक्शन से बाँधने के लिए, आप उपयोग करते हैं std::bind:

class MyClass
{
    public:
        MyClass();

        // Note: No longer marked `static`, and only takes the actual argument
        void Callback(int x);
    private:
        int private_x;
};

MyClass::MyClass()
{
    using namespace std::placeholders; // for `_1`

    private_x = 5;
    handler->addHandler(std::bind(&MyClass::Callback, this, _1));
}

void MyClass::Callback(int x)
{
    // No longer needs an explicit `instance` argument,
    // as `this` is set up properly
    cout << x + private_x << endl;
}

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

void freeStandingCallback(int x)
{
    // ...
}

int main()
{
    // ...
    handler->addHandler(freeStandingCallback);
}

इवेंट हैंडलर std::functionऑब्जेक्ट्स का उपयोग करने के बाद , नए C ++ 11 लैम्बडा फ़ंक्शन का उपयोग करना भी संभव बनाता है :

handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });

4
धन्यवाद जोआचिम! यह उदाहरण std :: function और std :: bind को ध्वस्त करने के लिए बहुत कुछ करता है। मैं निश्चित रूप से भविष्य में इसका इस्तेमाल करूंगा! संपादित करें मुझे अभी भी लैम्ब्डा नहीं मिलता है :)
बेंटएफ़एक्स

3
मैंने इसे अपनी बड़ी परियोजना (लगभग 6,000 पंक्तियों में बदल लिया है। यह मेरे लिए बहुत बड़ा है।) यह अलग-अलग कॉलबैक और मापदंडों के साथ बटन परिभाषाओं के वैक्टर का उपयोग करता है, फिर wxWidgets को फ़ीड करता है, ताकि ऑब्जेक्ट wxFrame में अपने स्वयं के बटन का प्रबंधन कर सकें। इसने चीजों को बहुत सरल कर दिया! मैं इसे पर्याप्त नहीं कह सकता, इंटरनेट में बहुत अधिक तकनीकीता और राय है, और पर्याप्त सरल उदाहरण नहीं हैं।
बेंटएफएक्स

1
@ user819640 कोई "अनबाइंड" नहीं है, इसके बजाय std::bindबस एक (अनिर्दिष्ट) वस्तु लौटाता है, और जब आप इसके साथ हो जाते हैं तो आप बस इसे दायरे से बाहर जाने दे सकते हैं। यदि बाध्य ऑब्जेक्ट को नष्ट कर दिया जाता है और आप फ़ंक्शन को कॉल करने का प्रयास करते हैं तो आपको अपरिभाषित व्यवहार मिलेगा ।
कुछ प्रोग्रामर

2
handler->addHandler(), इसका मतलब है कि कहीं आप एक वस्तु बनाते हैं EventHandler? अच्छा जवाब btw, +1।
gsamaras

1
ध्यान दें कि आपको तर्कों की संख्या से मिलान करने के लिए प्लेसहोल्डर्स की संख्या की आवश्यकता है, इसलिए यदि कॉलबैक में दो तर्क हैं तो आपको उपयोग करने की आवश्यकता है ...., _1, _2)और इसी तरह।
डेन-जेसन

5

यहाँ एक संक्षिप्त संस्करण है जो वर्ग विधि कॉलबैक के साथ और नियमित फ़ंक्शन कॉलबैक के साथ काम करता है। इस उदाहरण में, यह दिखाने के लिए कि मापदंडों को कैसे नियंत्रित किया जाता है, कॉलबैक फ़ंक्शन दो पैरामीटर लेता है: boolऔर int

class Caller {
  template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int))
  {
    using namespace std::placeholders; 
    callbacks_.emplace_back(std::bind(mf, object, _1, _2));
  }
  void addCallback(void(* const fun)(bool,int)) 
  {
    callbacks_.emplace_back(fun);
  }
  void callCallbacks(bool firstval, int secondval) 
  {
    for (const auto& cb : callbacks_)
      cb(firstval, secondval);
  }
private:
  std::vector<std::function<void(bool,int)>> callbacks_;
}

class Callee {
  void MyFunction(bool,int);
}

//then, somewhere in Callee, to add the callback, given a pointer to Caller `ptr`

ptr->addCallback(this, &Callee::MyFunction);

//or to add a call back to a regular function
ptr->addCallback(&MyRegularFunction);

यह C ++ 11-विशिष्ट कोड को AddCallback पद्धति और क्लास कॉलर में निजी डेटा को प्रतिबंधित करता है। मेरे लिए, कम से कम, इसे लागू करते समय गलतियाँ करने की संभावना को कम करता है।


3

आप जो करना चाहते हैं वह एक इंटरफ़ेस बनाना है जो इस कोड को संभालता है और आपके सभी वर्ग इंटरफ़ेस को लागू करते हैं।

class IEventListener{
public:
   void OnEvent(int x) = 0;  // renamed Callback to OnEvent removed the instance, you can add it back if you want.
};


class MyClass :public IEventListener
{
    ...
    void OnEvent(int x); //typically such a function is NOT static. This wont work if it is static.
};

class YourClass :public IEventListener
{

ध्यान दें कि "कॉलबैक" फ़ंक्शन को काम करने के लिए गैर स्थिर है जो मुझे लगता है कि एक सुधार है। यदि आप चाहते हैं कि यह स्थिर हो, तो आपको इसे करने की आवश्यकता है क्योंकि JaredC टेम्पलेट्स के साथ सुझाव देता है।


आप केवल इसका एक पक्ष दिखा रहे हैं। दिखाएँ कि कैसे आग लगने की घटना।
क्रिस्टोफर पिस

2

ऊपर दिए गए कोड से एक पूर्ण कार्य उदाहरण .... C ++ 11 के लिए:

#include <stdlib.h>
#include <stdio.h>
#include <functional>

#if __cplusplus <= 199711L
  #error This file needs at least a C++11 compliant compiler, try using:
  #error    $ g++ -std=c++11 ..
#endif

using namespace std;

class EventHandler {
    public:
        void addHandler(std::function<void(int)> callback) {
            printf("\nHandler added...");
            // Let's pretend an event just occured
            callback(1);
        }
};


class MyClass
{
    public:
        MyClass(int);
        // Note: No longer marked `static`, and only takes the actual argument
        void Callback(int x);

    private:
        EventHandler *pHandler;
        int private_x;
};

MyClass::MyClass(int value) {
    using namespace std::placeholders; // for `_1`

    pHandler = new EventHandler();
    private_x = value;
    pHandler->addHandler(std::bind(&MyClass::Callback, this, _1));
}

void MyClass::Callback(int x) {
    // No longer needs an explicit `instance` argument,
    // as `this` is set up properly
    printf("\nResult:%d\n\n", (x+private_x));
}

// Main method
int main(int argc, char const *argv[]) {

    printf("\nCompiler:%ld\n", __cplusplus);
    new MyClass(5);
    return 0;
}


// where $1 is your .cpp file name... this is the command used:
// g++ -std=c++11 -Wall -o $1 $1.cpp
// chmod 700 $1
// ./$1

आउटपुट होना चाहिए:

Compiler:201103

Handler added...
Result:6

1

MyClassऔर YourClassदोनों SomeonesClassको एक सार (आभासी) Callbackविधि से प्राप्त किया जा सकता है । आपका addHandlerप्रकार की वस्तुओं को स्वीकार करेंगे SomeonesClassऔर MyClassऔर YourClassओवरराइड कर सकते हैं Callbackकॉलबैक व्यवहार की उनकी विशिष्ट कार्यान्वयन प्रदान करने के लिए।


मैं जो कर रहा हूं उसके लिए मैं इस विचार के साथ आया हूं। लेकिन व्यापक रूप से विभिन्न वर्गों की संख्या के कारण जो मेरे हैंडलर का उपयोग कर रहे थे, मैंने इसे एक विकल्प के रूप में नहीं देखा था।
बेंटएफएक्स

0

यदि आपके पास विभिन्न मापदंडों के साथ कॉलबैक हैं, तो आप निम्नानुसार टेम्पलेट्स का उपयोग कर सकते हैं:
// के साथ संकलित करें: g ++ -std = c ++ 11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbackAppApp

#include <functional>     // c++11

#include <iostream>        // due to: cout


using std::cout;
using std::endl;

class MyClass
{
    public:
        MyClass();
        static void Callback(MyClass* instance, int x);
    private:
        int private_x;
};

class OtherClass
{
    public:
        OtherClass();
        static void Callback(OtherClass* instance, std::string str);
    private:
        std::string private_str;
};

class EventHandler
{

    public:
        template<typename T, class T2>
        void addHandler(T* owner, T2 arg2)
        {
            cout << "\nHandler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner, arg2);
         }   

};

MyClass::MyClass()
{
    EventHandler* handler;
    private_x = 4;
    handler->addHandler(this, private_x);
}

OtherClass::OtherClass()
{
    EventHandler* handler;
    private_str = "moh ";
    handler->addHandler(this, private_str );
}

void MyClass::Callback(MyClass* instance, int x)
{
    cout << " MyClass::Callback(MyClass* instance, int x) ==> " 
         << 6 + x + instance->private_x << endl;
}

void OtherClass::Callback(OtherClass* instance, std::string private_str)
{
    cout << " OtherClass::Callback(OtherClass* instance, std::string private_str) ==> " 
         << " Hello " << instance->private_str << endl;
}

int main(int argc, char** argv)
{
    EventHandler* handler;
    handler = new EventHandler();
    MyClass* myClass = new MyClass();
    OtherClass* myOtherClass = new OtherClass();
}

1
क्या आप कृपया बता सकते हैं कि आपने ओपी की समस्या को हल करने के लिए क्या किया? क्या ओपी का पूरा कोड शामिल करना वास्तव में आवश्यक है? ओपी चाहता था कि उसका कोड उसके साथ काम करे YourClass। आपको लगता है कि उस वर्ग को हटा दिया है और एक अलग जोड़ा है OtherClass। इसके अलावा, सवाल पहले से ही एक अच्छी तरह से जवाब मिल गया है। आपका समाधान कितना बेहतर है कि यह पोस्ट करने लायक है?
हार्न

मैंने नहीं कहा कि मेरी पोस्टिंग एक बेहतर समाधान है। मैंने दिखाया कि टेम्पलेट में "OtherClass" का उपयोग कैसे करें।
मोहद्दी 17
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.