ক্লাস সদস্য ব্যবহার করে সি ++ কলব্যাক


92

আমি জানি এটি অনেক বার জিজ্ঞাসা করা হয়েছে, এবং এর কারণে ক্রাফ্টের মাধ্যমে খনন করা এবং কী কাজ করে তার একটি সাধারণ উদাহরণ খুঁজে পাওয়া শক্ত।

আমি এটি পেয়েছি, এটি সহজ এবং এটি 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
কে তাত্পর্যপূর্ণ ফাংশন উদাহরণ পোস্ট করেছেন? আপনি চেকমার্কটি পেয়েছেন তবে আমি পরীক্ষার সময় আপনি নিজের উত্তরটি মুছলেন। এটি আমার যা প্রয়োজন ঠিক তা করেছে। একটি সাধারণ ফাংশন টেম্পলেট আমি যে সমস্ত অন্যান্য তথ্য পড়ছিলাম তার স্টুতে হারিয়ে গেল। আপনার উত্তরটি প্রশ্নের সম্পাদনা হিসাবে যুক্ত হয়েছে।
বেন্টএফএক্স

আমি মনে করি এটি জারেডসি ছিল। আপনাকে তাকে নীচে শিকার করার দরকার হতে পারে = পি
হুজক্রাইগ

উত্তর:


186

স্থিতিশীল পদ্ধতিগুলি না রেখে এবং ক্লাস উদাহরণে কোনও পয়েন্টারের আশেপাশে যাওয়ার পরিবর্তে আপনি নতুন সি ++ 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অবজেক্টস ব্যবহার করে, এটি নতুন সি ++ 11 ল্যাম্বদা ফাংশন ব্যবহার করাও সম্ভব করে তোলে :

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

4
ধন্যবাদ জোয়াছিম! এই উদাহরণটি std :: ফাংশন এবং স্ট্যান্ড :: বাইন্ডকে অনিষ্ট করতে অনেক কিছু করে। আমি অবশ্যই ভবিষ্যতে এটি ব্যবহার করব! সম্পাদনা করুন আমি এখনও
লাম্বদা মোটেও পাই

4
আমি এটিকে আমার বৃহত প্রকল্পে ভাঁজ করেছি (প্রায় lines,০০০ লাইন me এটি আমার পক্ষে বড় button এই সরলীকৃত জিনিসগুলি অনেক! আমি এটি যথেষ্ট বলতে পারি না, ইন্টারনেটে অনেক বেশি প্রযুক্তিগত এবং মতামত রয়েছে এবং যথেষ্ট সাধারণ উদাহরণ নেই।
বেন্টএফএক্স

4
@ user819640 কোনও "আনবাইন্ড" নেই, পরিবর্তে std::bindকেবল একটি (অনির্দিষ্ট) বস্তুটি ফেরত দেয় এবং আপনি এটি সম্পন্ন করার পরে আপনি কেবল এটিকে সুযোগের বাইরে যেতে দিতে পারেন। যদি সীমাবদ্ধ অবজেক্টটি ধ্বংস হয়ে যায় এবং আপনি ফাংশনটিতে কল করার চেষ্টা করেন আপনি অনির্ধারিত আচরণ পাবেন
কিছু প্রোগ্রামার ডিউড

4
handler->addHandler(), মানে কোথাও আপনি কোনও বস্তু তৈরি করেছেন EventHandler? ভাল উত্তর বিটিডাব্লু, +১।
gsamaras

4
নোট করুন যে আপনার আর্গুমেন্টের সংখ্যার সাথে মেলে তুলতে স্থানধারীর সংখ্যা প্রয়োজন, তাই যদি কলব্যাকে দুটি টি যুক্তি থাকে তবে আপনার ব্যবহারের প্রয়োজন ...., _1, _2)
ডেন-জেসন

6

এখানে একটি সংক্ষিপ্ত সংস্করণ যা শ্রেণি পদ্ধতি কলব্যাক এবং নিয়মিত ফাংশন কলব্যাকগুলির সাথে কাজ করে। এই উদাহরণে, পরামিতিগুলি কীভাবে পরিচালনা করা হয় তা দেখাতে, কলব্যাক ফাংশনটি দুটি পরামিতি নেয়: 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-নির্দিষ্ট কোডটি অ্যাডক্যালব্যাক পদ্ধতিতে এবং শ্রেণীর কলারের ব্যক্তিগত ডেটাতে সীমাবদ্ধ করে। আমার কাছে, কমপক্ষে, এটি কার্যকর করার সময় এটি ভুল করার সুযোগকে হ্রাস করে।


4

আপনি যা করতে চান তা হ'ল একটি ইন্টারফেস তৈরি করা যা এই কোডটি পরিচালনা করে এবং আপনার সমস্ত ক্লাস ইন্টারফেসটি প্রয়োগ করে।

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
{

নোট করুন যে এটির জন্য "কলব্যাক" ফাংশনটি অ স্থির যা আমি বিশ্বাস করি একটি উন্নতি। আপনি যদি এটি স্থির রাখতে চান তবে জ্যারেডিসি টেমপ্লেটগুলির সাথে পরামর্শ হিসাবে আপনার এটি করা দরকার।


আপনি কেবল এটির একটি দিক দেখিয়ে দিচ্ছেন। কীভাবে ইভেন্টটি ফায়ার করা যায় তা দেখান।
ক্রিস্টোফার পিজ

3

উপরের কোড থেকে সম্পূর্ণ কাজের উদাহরণ .... সি ++ 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 myTemplatedCPPcallbacks অ্যাপ্লিকেশন

#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();
}

4
ওপিটির সমস্যা সমাধানের জন্য আপনি কী করেছিলেন তা দয়া করে ব্যাখ্যা করতে পারেন? ওপির সম্পূর্ণ কোড অন্তর্ভুক্ত করা কি সত্যিই প্রয়োজনীয়? ওপি তার কোডটি তার সাথে কাজ করতে চেয়েছিল YourClass। আপনি এই ক্লাসটি সরিয়ে অন্যরকম যুক্ত করেছেন বলে মনে হচ্ছে OtherClass। তদুপরি, প্রশ্নটি ইতিমধ্যে একটি ভাল উত্তর পেয়েছে। আপনার সমাধানটি কতদূর ভাল যাতে এটি পোস্ট করার মতো?
হন্ক

আমি বলিনি যে আমার পোস্টিং আরও ভাল সমাধান। আমি টেম্পলেট উপায়ে "অন্যান্যক্লাস" কীভাবে ব্যবহার করব তা দেখিয়েছি।
মোহাদেডি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.