C ++ में कॉलबैक फ़ंक्शन


303

C ++ में, आप कॉलबैक फ़ंक्शन का उपयोग कब और कैसे करते हैं?

संपादित करें:
मैं कॉलबैक फ़ंक्शन लिखने के लिए एक सरल उदाहरण देखना चाहूंगा।


[यह] ( thispointer.com/… ) कॉलबैक फ़ंक्शन के बारे में मूल बातें बहुत अच्छी तरह से समझाता है और अवधारणा को समझना आसान है।
अनुराग सिंह

जवाबों:


449

नोट: अधिकांश उत्तर फंक्शन पॉइंटर्स को कवर करते हैं, जो C ++ में "कॉलबैक" लॉजिक को प्राप्त करने की एक संभावना है, लेकिन आज तक मेरे विचार से सबसे अनुकूल नहीं है।

कॉलबैक (?) क्या हैं और उनका उपयोग क्यों करना है (!)

एक कॉलबैक एक कॉल करने योग्य है (आगे देखें नीचे) एक वर्ग या फ़ंक्शन द्वारा स्वीकार किया जाता है, जिसका उपयोग उस कॉलबैक के आधार पर वर्तमान तर्क को अनुकूलित करने के लिए किया जाता है।

कॉलबैक का उपयोग करने का एक कारण सामान्य लिखना है कोड है, जिसे कॉल फ़ंक्शन में तर्क से स्वतंत्र है और विभिन्न कॉलबैक के साथ पुन: उपयोग किया जा सकता है।

मानक एल्गोरिदम पुस्तकालय के कई कार्य <algorithm>कॉलबैक का उपयोग करते हैं। उदाहरण के लिए for_eachएल्गोरिथ्म पुनरावृत्तियों की श्रेणी में प्रत्येक आइटम पर एक यूनिक कॉलबैक लागू करता है:

template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
  for (; first != last; ++first) {
    f(*first);
  }
  return f;
}

जिसका उपयोग पहले वेतन वृद्धि के लिए किया जा सकता है और फिर उदाहरण के लिए उपयुक्त कॉलबल्स पास करके एक वेक्टर प्रिंट करें:

std::vector<double> v{ 1.0, 2.2, 4.0, 5.5, 7.2 };
double r = 4.0;
std::for_each(v.begin(), v.end(), [&](double & v) { v += r; });
std::for_each(v.begin(), v.end(), [](double v) { std::cout << v << " "; });

जो प्रिंट करता है

5 6.2 8 9.5 11.2

कॉलबैक का एक अन्य अनुप्रयोग कुछ घटनाओं के कॉल करने वालों की सूचना है जो एक निश्चित मात्रा में स्थिर / संकलित समय लचीलेपन को सक्षम करता है।

व्यक्तिगत रूप से, मैं एक स्थानीय अनुकूलन पुस्तकालय का उपयोग करता हूं जो दो अलग-अलग कॉलबैक का उपयोग करता है:

  • पहला कॉलबैक कहा जाता है यदि एक फ़ंक्शन मान और इनपुट मानों के वेक्टर के आधार पर ग्रेडिएंट आवश्यक है (तर्क कॉलबैक: फ़ंक्शन मान निर्धारण / ग्रेडिएंट व्युत्पत्ति)।
  • दूसरा कॉलबैक प्रत्येक एल्गोरिदम कदम के लिए एक बार कहा जाता है और एल्गोरिदम (अधिसूचना कॉलबैक) के अभिसरण के बारे में कुछ जानकारी प्राप्त करता है।

इस प्रकार, लाइब्रेरी डिजाइनर यह तय करने के लिए प्रभारी नहीं है कि सूचना कॉलबैक के माध्यम से प्रोग्रामर को दी गई जानकारी के साथ क्या होता है और उसे इस बात की चिंता नहीं है कि वास्तव में फ़ंक्शन मान कैसे निर्धारित करें क्योंकि वे लॉजिक कॉलबैक द्वारा प्रदान किए गए हैं। उन चीजों को सही करना पुस्तकालय उपयोगकर्ता के कारण एक कार्य है और पुस्तकालय को पतला और अधिक सामान्य रखता है।

इसके अलावा, कॉलबैक गतिशील रनटाइम व्यवहार को सक्षम कर सकते हैं।

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

void player_jump();
void player_crouch();

class game_core
{
    std::array<void(*)(), total_num_keys> actions;
    // 
    void key_pressed(unsigned key_id)
    {
        if(actions[key_id]) actions[key_id]();
    }
    // update keybind from menu
    void update_keybind(unsigned key_id, void(*new_action)())
    {
        actions[key_id] = new_action;
    }
};

यहां फ़ंक्शन एक निश्चित कुंजी दबाए जाने पर वांछित व्यवहार प्राप्त करने के लिए key_pressedसंग्रहीत कॉलबैक का उपयोग करता actionsहै। यदि खिलाड़ी कूदने के लिए बटन बदलने का विकल्प चुनता है, तो इंजन कॉल कर सकता है

game_core_instance.update_keybind(newly_selected_key, &player_jump);

और इस प्रकार एक बटन के व्यवहार को बदल दें key_pressed(जिसे कॉल player_jump) एक बार इस बटन को दबाने के बाद अगली बार ingame।

C ++ (11) में कॉलबेल क्या हैं ?

C ++ अवधारणाओं को देखें : अधिक औपचारिक विवरण के लिए cppreference पर कॉल करने योग्य।

कॉलबैक कार्यक्षमता को C ++ (11) में कई तरीकों से महसूस किया जा सकता है क्योंकि कई अलग-अलग चीजें कॉल करने योग्य होती हैं * :

  • समारोह संकेत (सदस्य कार्यों के लिए संकेत सहित)
  • std::function वस्तुओं
  • लम्बोदर भाव
  • बाँधने का भाव
  • फ़ंक्शन ऑब्जेक्ट्स (अतिभारित फ़ंक्शन कॉल ऑपरेटर के साथ कक्षाएं operator())

* ध्यान दें: डेटा सदस्यों को इंगित करने के लिए कॉल करने योग्य हैं, लेकिन कोई फ़ंक्शन बिल्कुल नहीं कहा जाता है।

कॉलबैक लिखने के कई महत्वपूर्ण तरीके विस्तार से

  • X.1 "राइटिंग" इस पोस्ट में कॉलबैक प्रकार को घोषित करने और नाम देने के लिए वाक्यविन्यास का अर्थ है।
  • X.2 "कॉलिंग" एक कॉलबैक उन वस्तुओं को कॉल करने के लिए सिंटैक्स को संदर्भित करता है।
  • X.3 "कॉलबैक" का उपयोग कॉलबैक का उपयोग करके फ़ंक्शन में तर्क पास करते समय वाक्यविन्यास का अर्थ है।

नोट: C ++ 17 के अनुसार, एक कॉल की तरह f(...)लिखा जा सकता है, std::invoke(f, ...)जो कि पॉइंटर टू मेंबर केस को भी हैंडल करता है।

1. कार्य बिंदु

एक फ़ंक्शन पॉइंटर 'सबसे सरल' है (सामान्यता के संदर्भ में, पठनीयता के संदर्भ में यकीनन सबसे खराब) एक कॉलबैक हो सकता है।

चलो एक सरल समारोह है foo:

int foo (int x) { return 2+x; }

1.1 फ़ंक्शन पॉइंटर / टाइप नोटेशन लिखना

एक फ़ंक्शन पॉइंटर प्रकार में अंकन है

return_type (*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to foo has the type:
int (*)(int)

जहां एक नामित फ़ंक्शन पॉइंटर प्रकार दिखेगा

return_type (* name) (parameter_type_1, parameter_type_2, parameter_type_3)

// i.e. f_int_t is a type: function pointer taking one int argument, returning int
typedef int (*f_int_t) (int); 

// foo_p is a pointer to function taking int returning int
// initialized by pointer to function foo taking int returning int
int (* foo_p)(int) = &foo; 
// can alternatively be written as 
f_int_t foo_p = &foo;

usingघोषणा हमें, मेकअप बातें थोड़ा और अधिक पठनीय का विकल्प देता है के बाद से typedefके लिए f_int_tके रूप में भी लिखा जा सकता है:

using f_int_t = int(*)(int);

जहां (कम से कम मेरे लिए) यह स्पष्ट है कि f_int_tनए प्रकार का उपनाम है और फ़ंक्शन पॉइंटर प्रकार की मान्यता भी आसान है

और फ़ंक्शन पॉइंटर प्रकार के कॉलबैक का उपयोग करके एक फ़ंक्शन की घोषणा होगी:

// foobar having a callback argument named moo of type 
// pointer to function returning int taking int as its argument
int foobar (int x, int (*moo)(int));
// if f_int is the function pointer typedef from above we can also write foobar as:
int foobar (int x, f_int_t moo);

1.2 कॉलबैक कॉल नोटेशन

कॉल नोटेशन सरल फ़ंक्शन कॉल सिंटैक्स का अनुसरण करता है:

int foobar (int x, int (*moo)(int))
{
    return x + moo(x); // function pointer moo called using argument x
}
// analog
int foobar (int x, f_int_t moo)
{
    return x + moo(x); // function pointer moo called using argument x
}

1.3 कॉलबैक नोटेशन और संगत प्रकारों का उपयोग करता है

एक फंक्शन पॉइंटर लेने वाले कॉलबैक फ़ंक्शन को फ़ंक्शन पॉइंटर्स का उपयोग करके बुलाया जा सकता है।

एक फ़ंक्शन पॉइंटर कॉलबैक लेने वाले फ़ंक्शन का उपयोग करना सरल है:

 int a = 5;
 int b = foobar(a, foo); // call foobar with pointer to foo as callback
 // can also be
 int b = foobar(a, &foo); // call foobar with pointer to foo as callback

1.4 उदाहरण

एक फंक्शन सीए लिखा जाना चाहिए जो इस बात पर भरोसा नहीं करता है कि कॉलबैक कैसे काम करता है:

void tranform_every_int(int * v, unsigned n, int (*fp)(int))
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

जहां संभव कॉलबैक हो सकता है

int double_int(int x) { return 2*x; }
int square_int(int x) { return x*x; }

जैसे इस्तेमाल किया

int a[5] = {1, 2, 3, 4, 5};
tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};
tranform_every_int(&a[0], 5, square_int);
// now a == {4, 16, 36, 64, 100};

2. सदस्य समारोह के लिए सूचक

फंक्शन टू मेम्बर फंक्शन (कुछ क्लास का C) एक खास तरह का (और उससे भी ज्यादा कॉम्प्लेक्स) फंक्शन पॉइंटर होता है, जिसे Cऑपरेट करने के लिए किसी ऑब्जेक्ट की टाइप की जरूरत होती है।

struct C
{
    int y;
    int foo(int x) const { return x+y; }
};

2.1 सदस्य कार्य / प्रकार संकेतन के लिए सूचक लिखना

कुछ वर्ग के लिए सदस्य प्रकार के एक सूचकT में संकेतन होता है

// can have more or less parameters
return_type (T::*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to C::foo has the type
int (C::*) (int)

जहाँ एक नामित पॉइंटर को सदस्य फ़ंक्शन में- फ़ंक्शन फ़ंक्शन सूचक के अनुरूप होगा- इस तरह देखें:

return_type (T::* name) (parameter_type_1, parameter_type_2, parameter_type_3)

// i.e. a type `f_C_int` representing a pointer to member function of `C`
// taking int returning int is:
typedef int (C::* f_C_int_t) (int x); 

// The type of C_foo_p is a pointer to member function of C taking int returning int
// Its value is initialized by a pointer to foo of C
int (C::* C_foo_p)(int) = &C::foo;
// which can also be written using the typedef:
f_C_int_t C_foo_p = &C::foo;

उदाहरण: सदस्य फ़ंक्शन कॉलबैक के लिए पॉइंटर को अपने तर्कों में से एक के रूप में घोषित करना :

// C_foobar having an argument named moo of type pointer to member function of C
// where the callback returns int taking int as its argument
// also needs an object of type c
int C_foobar (int x, C const &c, int (C::*moo)(int));
// can equivalently declared using the typedef above:
int C_foobar (int x, C const &c, f_C_int_t moo);

2.2 कॉलबैक कॉल नोटेशन

सदस्य फ़ंक्शन के सूचक को dereferenced पॉइंटर पर सदस्य एक्सेस ऑपरेशंस का उपयोग करके Cप्रकार के ऑब्जेक्ट के संबंध में आमंत्रित किया जा सकता है Cनोट: कोष्ठक की आवश्यकता!

int C_foobar (int x, C const &c, int (C::*moo)(int))
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}
// analog
int C_foobar (int x, C const &c, f_C_int_t moo)
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}

नोट: यदि कोई पॉइंटर Cउपलब्ध है तो सिंटैक्स समतुल्य है (जहां पॉइंटर को Cआवश्यक रूप से डीरेल किया जाना चाहिए):

int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + ((*c).*meow)(x); 
}
// or equivalent:
int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + (c->*meow)(x); 
}

2.3 कॉलबैक नोटेशन और संगत प्रकारों का उपयोग करता है

एक कॉलबैक फ़ंक्शन को क्लास के Tएक सदस्य फ़ंक्शन पॉइंटर को सदस्य फ़ंक्शन पॉइंटर का उपयोग करके बुलाया जा सकता है T

एक फ़ंक्शन का उपयोग करना जो सदस्य फ़ंक्शन कॉलबैक के लिए एक पॉइंटर लेता है, यह फ़ंक्शन पॉइंटर्स के अनुरूप है- काफी सरल:

 C my_c{2}; // aggregate initialization
 int a = 5;
 int b = C_foobar(a, my_c, &C::foo); // call C_foobar with pointer to foo as its callback

3. std::functionवस्तुएं (हेडर<functional> )

std::functionवर्ग की दुकान, नकल करने के लिए या callables आह्वान एक बहुरूपी समारोह आवरण है।

३.१ एक std::functionवस्तु / प्रकार संकेतन लिखना

std::functionकिसी कॉल करने योग्य वस्तु को संग्रहीत करने का प्रकार दिखता है:

std::function<return_type(parameter_type_1, parameter_type_2, parameter_type_3)>

// i.e. using the above function declaration of foo:
std::function<int(int)> stdf_foo = &foo;
// or C::foo:
std::function<int(const C&, int)> stdf_C_foo = &C::foo;

3.2 कॉलबैक कॉल नोटेशन

वर्ग std::functionने operator()परिभाषित किया है जिसका उपयोग अपने लक्ष्य को प्राप्त करने के लिए किया जा सकता है।

int stdf_foobar (int x, std::function<int(int)> moo)
{
    return x + moo(x); // std::function moo called
}
// or 
int stdf_C_foobar (int x, C const &c, std::function<int(C const &, int)> moo)
{
    return x + moo(c, x); // std::function moo called using c and x
}

3.3 कॉलबैक नोटेशन और संगत प्रकारों का उपयोग करें

std::functionकॉलबैक फ़ंक्शन संकेत दिए गए या सदस्य कार्य करने के लिए सूचक की तुलना में अधिक सामान्य है के बाद से विभिन्न प्रकार के पारित किया जा सकता है और परोक्ष एक में बदल दियाstd::function वस्तु।

3.3.1 सदस्य कार्यों के लिए संकेत और संकेत

एक फ़ंक्शन पॉइंटर

int a = 2;
int b = stdf_foobar(a, &foo);
// b == 6 ( 2 + (2+2) )

या सदस्य समारोह के लिए एक सूचक

int a = 2;
C my_c{7}; // aggregate initialization
int b = stdf_C_foobar(a, c, &C::foo);
// b == 11 == ( 2 + (7+2) )

इस्तेमाल किया जा सकता है।

३.३.२ लभ्दा भाव

एक लंबोदर अभिव्यक्ति से एक अनाम बंद std::functionवस्तु में संग्रहीत किया जा सकता है :

int a = 2;
int c = 3;
int b = stdf_foobar(a, [c](int x) -> int { return 7+c*x; });
// b == 15 ==  a + (7*c*a) == 2 + (7+3*2)

३.३.३ std::bindभाव

एक std::bindअभिव्यक्ति का परिणाम पारित किया जा सकता है। एक फ़ंक्शन पॉइंटर कॉल के लिए बाध्यकारी मापदंडों द्वारा उदाहरण के लिए:

int foo_2 (int x, int y) { return 9*x + y; }
using std::placeholders::_1;

int a = 2;
int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
// b == 23 == 2 + ( 9*2 + 3 )
int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
// c == 49 == 2 + ( 9*5 + 2 )

जहाँ वस्तुएं सदस्य कार्यों के लिए सूचक के आह्वान के लिए वस्तु के रूप में बंधी हो सकती हैं:

int a = 2;
C const my_c{7}; // aggregate initialization
int b = stdf_foobar(a, std::bind(&C::foo, my_c, _1));
// b == 1 == 2 + ( 2 + 7 )

3.3.4 फ़ंक्शन ऑब्जेक्ट

एक उचित operator()अधिभार वाले वर्गों की वस्तुओं को एक std::functionवस्तु के अंदर भी संग्रहीत किया जा सकता है ।

struct Meow
{
  int y = 0;
  Meow(int y_) : y(y_) {}
  int operator()(int x) { return y * x; }
};
int a = 11;
int b = stdf_foobar(a, Meow{8});
// b == 99 == 11 + ( 8 * 11 )

३.४ उदाहरण

उपयोग करने के लिए फ़ंक्शन पॉइंटर उदाहरण को बदलना std::function

void stdf_tranform_every_int(int * v, unsigned n, std::function<int(int)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

उस फ़ंक्शन को पूरी तरह से अधिक उपयोगिता देता है क्योंकि (3.3 देखें) हमारे पास इसका उपयोग करने की अधिक संभावनाएं हैं:

// using function pointer still possible
int a[5] = {1, 2, 3, 4, 5};
stdf_tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};

// use it without having to write another function by using a lambda
stdf_tranform_every_int(&a[0], 5, [](int x) -> int { return x/2; });
// now a == {1, 2, 3, 4, 5}; again

// use std::bind :
int nine_x_and_y (int x, int y) { return 9*x + y; }
using std::placeholders::_1;
// calls nine_x_and_y for every int in a with y being 4 every time
stdf_tranform_every_int(&a[0], 5, std::bind(nine_x_and_y, _1, 4));
// now a == {13, 22, 31, 40, 49};

4. अस्थायी कॉलबैक प्रकार

टेम्प्लेट का उपयोग करके, कॉलबैक कॉलिंग कोड std::functionवस्तुओं का उपयोग करने से भी अधिक सामान्य हो सकता है ।

ध्यान दें कि टेम्पलेट एक संकलन-समय की विशेषता है और संकलन-समय के बहुरूपता के लिए एक डिज़ाइन उपकरण है। यदि कॉलबैक के माध्यम से रनटाइम डायनेमिक व्यवहार प्राप्त किया जाना है, तो टेम्पलेट मदद करेंगे लेकिन वे रनटाइम डायनेमिक्स को प्रेरित नहीं करेंगे।

4.1 लेखन (टाइप नोटेशन) और टेम्पर्ड कॉलबैक कॉलिंग

सामान्यीकरण अर्थात std_ftransform_every_intऊपर से कोड को भी टेम्प्लेट का उपयोग करके प्राप्त किया जा सकता है:

template<class R, class T>
void stdf_transform_every_int_templ(int * v,
  unsigned const n, std::function<R(T)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

एक कॉलबैक प्रकार के लिए एक और भी सामान्य (साथ ही सबसे आसान) सिंटैक्स के साथ एक सादा, टू-बीडेड टेंपरेचर:

template<class F>
void transform_every_int_templ(int * v, 
  unsigned const n, F f)
{
  std::cout << "transform_every_int_templ<" 
    << type_name<F>() << ">\n";
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = f(v[i]);
  }
}

नोट: शामिल आउटपुट टेम्पर्ड टाइप के लिए घटा हुआ नाम टाइप करता है F। का कार्यान्वयन type_nameइस पद के अंत में दिया गया है।

किसी श्रेणी के एकात्मक परिवर्तन के लिए सबसे सामान्य कार्यान्वयन मानक पुस्तकालय का हिस्सा है, अर्थात् std::transform, जिसे पुनरावृत्त प्रकारों के संबंध में भी रखा गया है।

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
  UnaryOperation unary_op)
{
  while (first1 != last1) {
    *d_first++ = unary_op(*first1++);
  }
  return d_first;
}

4.2 टेम्प्लेटेड कॉलबैक और संगत प्रकारों का उपयोग करते हुए उदाहरण

std::functionटेम्पर्ड कॉलबैक विधि के लिए संगत प्रकार stdf_transform_every_int_templउपर्युक्त प्रकारों के समान हैं (देखें 3.4)।

हालांकि टेम्प्लेटेड संस्करण का उपयोग करना, उपयोग किए गए कॉलबैक के हस्ताक्षर में थोड़ा बदलाव हो सकता है:

// Let
int foo (int x) { return 2+x; }
int muh (int const &x) { return 3+x; }
int & woof (int &x) { x *= 4; return x; }

int a[5] = {1, 2, 3, 4, 5};
stdf_transform_every_int_templ<int,int>(&a[0], 5, &foo);
// a == {3, 4, 5, 6, 7}
stdf_transform_every_int_templ<int, int const &>(&a[0], 5, &muh);
// a == {6, 7, 8, 9, 10}
stdf_transform_every_int_templ<int, int &>(&a[0], 5, &woof);

नोट: std_ftransform_every_int(गैर टेम्प्लेटेड संस्करण; ऊपर देखें) fooउपयोग नहीं करने के साथ काम करता है muh

// Let
void print_int(int * p, unsigned const n)
{
  bool f{ true };
  for (unsigned i = 0; i < n; ++i)
  {
    std::cout << (f ? "" : " ") << p[i]; 
    f = false;
  }
  std::cout << "\n";
}

सादे टेम्पर्ड पैरामीटर transform_every_int_templहर संभव कॉल करने योग्य प्रकार हो सकता है।

int a[5] = { 1, 2, 3, 4, 5 };
print_int(a, 5);
transform_every_int_templ(&a[0], 5, foo);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, muh);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, woof);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, [](int x) -> int { return x + x + x; });
print_int(a, 5);
transform_every_int_templ(&a[0], 5, Meow{ 4 });
print_int(a, 5);
using std::placeholders::_1;
transform_every_int_templ(&a[0], 5, std::bind(foo_2, _1, 3));
print_int(a, 5);
transform_every_int_templ(&a[0], 5, std::function<int(int)>{&foo});
print_int(a, 5);

उपरोक्त कोड प्रिंट:

1 2 3 4 5
transform_every_int_templ <int(*)(int)>
3 4 5 6 7
transform_every_int_templ <int(*)(int&)>
6 8 10 12 14
transform_every_int_templ <int& (*)(int&)>
9 11 13 15 17
transform_every_int_templ <main::{lambda(int)#1} >
27 33 39 45 51
transform_every_int_templ <Meow>
108 132 156 180 204
transform_every_int_templ <std::_Bind<int(*(std::_Placeholder<1>, int))(int, int)>>
975 1191 1407 1623 1839
transform_every_int_templ <std::function<int(int)>>
977 1193 1409 1625 1841

type_name कार्यान्वयन ऊपर इस्तेमाल किया

#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cxxabi.h>

template <class T>
std::string type_name()
{
  typedef typename std::remove_reference<T>::type TR;
  std::unique_ptr<char, void(*)(void*)> own
    (abi::__cxa_demangle(typeid(TR).name(), nullptr,
    nullptr, nullptr), std::free);
  std::string r = own != nullptr?own.get():typeid(TR).name();
  if (std::is_const<TR>::value)
    r += " const";
  if (std::is_volatile<TR>::value)
    r += " volatile";
  if (std::is_lvalue_reference<T>::value)
    r += " &";
  else if (std::is_rvalue_reference<T>::value)
    r += " &&";
  return r;
}

35
@BogeyJammer: यदि आपने ध्यान नहीं दिया है: उत्तर के दो भाग हैं। 1. एक छोटे उदाहरण के साथ "कॉलबैक" की एक सामान्य व्याख्या। 2. विभिन्न कॉलबलों की एक व्यापक सूची और कॉलबैक का उपयोग करके कोड लिखने के तरीके। आप विस्तार से खुदाई नहीं करने या पूरे उत्तर को पढ़ने का स्वागत करते हैं, लेकिन सिर्फ इसलिए कि आप एक विस्तृत दृश्य नहीं चाहते हैं, यह ऐसा नहीं है कि उत्तर अप्रभावी हो या "क्रूरता से नकल"। विषय "सी ++ कॉलबैक" है। भले ही भाग 1 ओपी के लिए ठीक है, दूसरों को भाग 2 उपयोगी लग सकता है। -1 के बजाय पहले भाग के लिए किसी भी जानकारी या रचनात्मक आलोचना की कमी महसूस करें।
Pixelchemist

1
भाग 1 शुरुआती अनुकूल और पर्याप्त स्पष्ट नहीं है। मैं यह कहकर अधिक रचनात्मक नहीं हो सकता कि यह मुझे कुछ सीखने में कामयाब नहीं हुआ। और भाग 2, का अनुरोध नहीं किया गया था, पृष्ठ को भरना और सवाल से बाहर है, भले ही आप यह दिखाते हैं कि यह उपयोगी है, इस तथ्य के बावजूद कि यह आमतौर पर समर्पित प्रलेखन में पाया जाता है, जहां इस तरह की विस्तृत जानकारी पहले स्थान पर देखी जाती है। मैं निश्चित रूप से गिरावट को बनाए रखता हूं। एक एकल वोट एक व्यक्तिगत राय का प्रतिनिधित्व करता है इसलिए कृपया इसे स्वीकार करें और इसका सम्मान करें।
बोगी जैमर

24
@BogeyJammer मैं प्रोग्रामिंग में नया नहीं हूँ, लेकिन मैं "आधुनिक c ++" के लिए नया हूँ। यह उत्तर मुझे सटीक संदर्भ देता है, जो मुझे विशेष रूप से, c ++ में भूमिका कॉलबैक की भूमिका के बारे में तर्क करने की आवश्यकता है। ओपी ने कई उदाहरणों के लिए नहीं पूछा हो सकता है, लेकिन यह एसओ पर प्रथागत है, मूर्खों की दुनिया को शिक्षित करने के लिए एक कभी न खत्म होने वाली खोज में, एक प्रश्न के सभी संभावित समाधानों की गणना करना। अगर यह एक किताब की तरह बहुत अधिक पढ़ता है तो मैं केवल कुछ ही पढ़कर थोड़ा सा अभ्यास करने की सलाह दे सकता हूं
dcow

int b = foobar(a, foo); // call foobar with pointer to foo as callback, यह एक टाइपो सही है? fooयह AFAIK काम करने के लिए एक सूचक होना चाहिए।
कोनोफो

@konoufo: [conv.func]C ++ 11 मानक का कहना है: " फ़ंक्शन प्रकार टी का एक अंतराल" टाइप्टर टू टी। " परिणाम फ़ंक्शन के लिए एक संकेतक है। "यह एक मानक रूपांतरण है और जैसा कि अंतर्निहित रूप से होता है। एक (निश्चित रूप से) यहाँ फ़ंक्शन पॉइंटर का उपयोग कर सकता है।
Pixelchemist

160

कॉलबैक करने का C तरीका भी है: फंक्शन पॉइंटर्स

//Define a type for the callback signature,
//it is not necessary, but makes life easier

//Function pointer called CallbackType that takes a float
//and returns an int
typedef int (*CallbackType)(float);  


void DoWork(CallbackType callback)
{
  float variable = 0.0f;

  //Do calculations

  //Call the callback with the variable, and retrieve the
  //result
  int result = callback(variable);

  //Do something with the result
}

int SomeCallback(float variable)
{
  int result;

  //Interpret variable

  return result;
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWork(&SomeCallback);
}

अब यदि आप कॉलबैक के रूप में कक्षा के तरीकों में उत्तीर्ण होना चाहते हैं, तो उन फ़ंक्शन पॉइंटर्स की घोषणाओं में अधिक जटिल घोषणाएं हैं, उदाहरण:

//Declaration:
typedef int (ClassName::*CallbackType)(float);

//This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
  //Class instance to invoke it through
  ClassName objectInstance;

  //Invocation
  int result = (objectInstance.*callback)(1.0f);
}

//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
  //Class pointer to invoke it through
  ClassName * pointerInstance;

  //Invocation
  int result = (pointerInstance->*callback)(1.0f);
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWorkObject(&ClassName::Method);
  DoWorkPointer(&ClassName::Method);
}

1
वर्ग विधि उदाहरण में कोई त्रुटि है। मंगलाचरण होना चाहिए: (उदाहरण। * कॉलबैक) (1.0f)
कार्ल जॉन्सन

उसे इंगित करने के लिए धन्यवाद। मैं एक वस्तु के माध्यम से, और एक वस्तु सूचक के माध्यम से वर्णन करने के लिए दोनों जोड़ूंगा।
रेमन ज़राज़ुआ बी।

3
यह std :: tr1: से कार्य का नुकसान है, जिसमें कॉलबैक प्रति-वर्ग टाइप किया गया है; यह सी-स्टाइल कॉलबैक का उपयोग करने के लिए अव्यवहारिक बनाता है जब कॉल करने वाले ऑब्जेक्ट को कॉल किए जाने वाले ऑब्जेक्ट के वर्ग का पता नहीं होता है।
3

मैं typedefकॉलबैक प्रकार के बिना इसे कैसे कर सकता हूं ? क्या यह भी संभव है?
टॉम ज़ातो -

1
हाँ तुम कर सकते हो। typedefइसे और अधिक पठनीय बनाने के लिए सिंटैक्टिक शुगर है। बिना typedef, फंक्शन पॉइंटर्स के लिए DoWorkObject की परिभाषा होगी void DoWorkObject(int (*callback)(float)):। सदस्य void DoWorkObject(int (ClassName::*callback)(float))
बिंदुओं के लिए

68

स्कॉट मेयर्स एक अच्छा उदाहरण देता है:

class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
  typedef std::function<int (const GameCharacter&)> HealthCalcFunc;

  explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
  : healthFunc(hcf)
  { }

  int healthValue() const { return healthFunc(*this); }

private:
  HealthCalcFunc healthFunc;
};

मुझे लगता है कि उदाहरण यह सब कहता है।

std::function<> C ++ कॉलबैक लिखने का "आधुनिक" तरीका है।


1
ब्याज से बाहर, एसएम किस पुस्तक में यह उदाहरण देता है? चीयर्स :)
सैम-डब्ल्यू-

5
मुझे पता है कि यह पुराना है, लेकिन क्योंकि मैंने लगभग ऐसा करना शुरू कर दिया है और यह मेरे सेटअप (mingw) पर काम नहीं कर रहा है, यदि आप जीसीसी संस्करण <4.x का उपयोग कर रहे हैं, तो यह विधि समर्थित नहीं है। कुछ निर्भरताएँ जो मैं उपयोग कर रहा हूँ, जीसीसी संस्करण> = 4.0.1 में बहुत सारे काम के बिना संकलित नहीं होगी, इसलिए मैं अच्छे पुराने जमाने के सी-स्टाइल कॉलबैक का उपयोग कर रहा हूं, जो ठीक काम करते हैं।
ओजबैरी

38

एक कॉलबैक समारोह के लिए एक विधि है कि एक दिनचर्या में पारित हो जाता है, और कहा जाता है दिनचर्या जो करने के लिए इसे पारित कर दिया है द्वारा कुछ बिंदु पर है।

पुन: प्रयोज्य सॉफ्टवेयर बनाने के लिए यह बहुत उपयोगी है। उदाहरण के लिए, कई ऑपरेटिंग सिस्टम एपीआई (जैसे विंडोज एपीआई) कॉलबैक का भारी उपयोग करते हैं।

उदाहरण के लिए, यदि आप किसी फ़ोल्डर में फ़ाइलों के साथ काम करना चाहते हैं - तो आप अपनी दिनचर्या के साथ एक एपीआई फ़ंक्शन को कॉल कर सकते हैं, और आपकी दिनचर्या निर्दिष्ट फ़ोल्डर में प्रति फ़ाइल एक बार चलती है। यह एपीआई को बहुत लचीला बनाने की अनुमति देता है।


63
यह जवाब वास्तव में औसत प्रोग्रामर कुछ भी नहीं बताता है जो वह नहीं जानता था। मैं कई अन्य भाषाओं से परिचित होते हुए C ++ सीख रहा हूं। सामान्य तौर पर जो कॉलबैक होता है वह मुझे चिंतित नहीं करता है।
टॉम ज़ातो -

17

स्वीकृत उत्तर बहुत उपयोगी और काफी व्यापक है। हालांकि, ओपी बताता है

मैं कॉलबैक फ़ंक्शन लिखने के लिए एक सरल उदाहरण देखना चाहूंगा ।

तो यहां आप जाते हैं, C ++ 11 से आपके पास std::functionइतना है कि फ़ंक्शन पॉइंटर्स और समान सामान की कोई आवश्यकता नहीं है:

#include <functional>
#include <string>
#include <iostream>

void print_hashes(std::function<int (const std::string&)> hash_calculator) {
    std::string strings_to_hash[] = {"you", "saved", "my", "day"};
    for(auto s : strings_to_hash)
        std::cout << s << ":" << hash_calculator(s) << std::endl;    
}

int main() {
    print_hashes( [](const std::string& str) {   /** lambda expression */
        int result = 0;
        for (int i = 0; i < str.length(); i++)
            result += pow(31, i) * str.at(i);
        return result;
    });
    return 0;
}

यह उदाहरण किसी तरह वास्तविक है, क्योंकि आप print_hashesहैश फ़ंक्शन के विभिन्न कार्यान्वयनों के साथ फ़ंक्शन को कॉल करना चाहते हैं , इस उद्देश्य के लिए मैंने एक सरल प्रदान किया। यह एक स्ट्रिंग प्राप्त करता है, एक इंट (प्रदान की गई स्ट्रिंग का एक हैश मान) देता है, और आपको सिंटैक्स भाग से याद रखने की आवश्यकता है std::function<int (const std::string&)>जो फ़ंक्शन के इनपुट तर्क के रूप में ऐसे फ़ंक्शन का वर्णन करता है जो इसे लागू करेगा।


उपरोक्त सभी उत्तरों में से, इसने मुझे समझा कि कॉलबैक क्या हैं और उनका उपयोग कैसे किया जाए। धन्यवाद।
मेहर चरण सहाय

@MeharCharanSahai इसे सुनकर खुशी हुई :) आपका स्वागत है।
मिलजेन मिकिक

9

C ++ में कॉलबैक फ़ंक्शन की स्पष्ट अवधारणा नहीं है। कॉलबैक मैकेनिज्म को अक्सर फंक्शन पॉइंटर्स, फन्नेटर ऑब्जेक्ट्स या कॉलबैक ऑब्जेक्ट्स के माध्यम से लागू किया जाता है। प्रोग्रामर को स्पष्ट रूप से कॉलबैक कार्यक्षमता को डिजाइन और कार्यान्वित करना होगा।

फीडबैक के आधार पर संपादित करें:

नकारात्मक प्रतिक्रिया के बावजूद यह जवाब मिला है, यह गलत नहीं है। मैं यह समझाने की कोशिश करूँगा कि मैं कहाँ से आ रहा हूँ।

C और C ++ में कॉलबैक फ़ंक्शंस को लागू करने की आवश्यकता है। कॉलबैक फ़ंक्शन को लागू करने का सबसे आम और तुच्छ तरीका एक फ़ंक्शन सूचक को फ़ंक्शन तर्क के रूप में पास करना है।

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

उदाहरण के लिए, IFormatProvider के लिए .NET प्रलेखन कहता है कि "GetFormat एक कॉलबैक विधि है" , भले ही यह केवल एक रन-ऑफ-द-मिल इंटरफ़ेस विधि है। मुझे नहीं लगता कि कोई भी तर्क देगा कि सभी वर्चुअल विधि कॉलबैक फ़ंक्शन हैं। क्या करता है GetFormat एक कॉलबैक विधि यह कैसे पारित या आह्वान किया है के यांत्रिकी नहीं है, लेकिन कॉल करने वाले के अर्थ के अर्थ क्या GetFormat विधि कहा जाएगा।

कुछ भाषाओं में स्पष्ट कॉलबैक शब्दार्थ के साथ विशेषताएं शामिल होती हैं, आमतौर पर घटनाओं और इवेंट हैंडलिंग से संबंधित होती हैं। उदाहरण के लिए, C # में सिंटैक्स और शब्दार्थ के साथ घटना प्रकार स्पष्ट रूप से कॉलबैक की अवधारणा के आसपास डिज़ाइन किया गया है। विजुअल बेसिक के पास अपने हैंडल क्लॉज हैं, जो स्पष्ट रूप से प्रतिनिधियों या फ़ंक्शन पॉइंटर्स की अवधारणा को दूर करते हुए कॉलबैक फ़ंक्शन होने की विधि की घोषणा करता है। इन मामलों में, कॉलबैक की शब्दार्थ अवधारणा को भाषा में ही एकीकृत किया जाता है।

दूसरी ओर, C और C ++, कॉलबैक फ़ंक्शंस की अर्थ संबंधी अवधारणा को लगभग स्पष्ट रूप से एम्बेड नहीं करता है । तंत्र वहां हैं, एकीकृत शब्दार्थ नहीं हैं। आप कॉलबैक फ़ंक्शंस को ठीक से लागू कर सकते हैं, लेकिन कुछ और अधिक परिष्कृत करने के लिए जिसमें स्पष्ट कॉलबैक शब्दार्थ शामिल हैं, जिसे आपको C ++ द्वारा प्रदान किए गए शीर्ष पर बनाना होगा, जैसे कि क्यूटी ने अपने सिग्नल और स्लॉट्स के साथ क्या किया था ।

संक्षेप में, C ++ के पास कॉलबैक को लागू करने की आवश्यकता है, जो अक्सर फ़ंक्शन पॉइंटर्स का उपयोग करके बहुत आसानी से और तुच्छ रूप से होता है। यह क्या नहीं है कीवर्ड और विशेषताएं हैं जिनके शब्दार्थ कॉलबैक के लिए विशिष्ट हैं, जैसे कि उठाना , उत्सर्जन , हैंडल , ईवेंट + = , आदि। यदि आप उन प्रकार के तत्वों के साथ भाषा से आ रहे हैं, तो C ++ में मूल कॉलबैक समर्थन। महसूस होगा।


1
सौभाग्य से यह पहला उत्तर नहीं था जब मैंने इस पृष्ठ का दौरा किया, अन्यथा मैंने तत्काल उछाल दिया!
ubugnu

6

कॉलबैक फ़ंक्शन C मानक का हिस्सा हैं, इसलिए C ++ का भी हिस्सा है। लेकिन अगर आप C ++ के साथ काम कर रहे हैं, तो मैं आपको इसके बजाय पर्यवेक्षक पैटर्न का उपयोग करने का सुझाव दूंगा : http://en.wikipedia.org/wiki/Observer_pattern


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

3
"सी मानक का हिस्सा, इसलिए सी ++ का भी हिस्सा है।" यह एक सामान्य गलतफहमी है, लेकिन फिर भी एक गलतफहमी है :-)
सीमित प्रायश्चित

मुझे सहमत होना होगा। मैं इसे वैसे ही छोड़ दूंगा, क्योंकि अगर मैं इसे बदलूंगा तो यह केवल और अधिक भ्रम पैदा करेगा। मेरा कहने का मतलब था कि फंक्शन पॉइंटर (!) मानक का हिस्सा है। इससे अलग कुछ भी कहना - मैं मानता हूं - भ्रामक है।
ऑडियोडायर

कॉलबैक फ़ंक्शन किस तरह से "सी मानक का हिस्सा" हैं? मैं इस तथ्य को नहीं समझता कि यह कार्यों के लिए कार्यों और बिंदुओं का समर्थन करता है, इसका मतलब है कि यह विशेष रूप से भाषा की अवधारणा के रूप में कॉलबैक को रद्द कर सकता है। इसके अलावा, जैसा कि उल्लेख किया गया है, यह सीधे सी ++ के लिए प्रासंगिक नहीं होगा, भले ही यह सटीक था। और यह विशेष रूप से प्रासंगिक नहीं है जब ओपी ने C ++ (एक लंगड़ा, बहुत व्यापक प्रश्न, लेकिन फिर भी) में कॉलबैक का उपयोग करने के लिए "कब और कैसे" पूछा, और आपका जवाब लिंक के लिए कुछ अलग करने के लिए केवल एक सलाह है।
अंडरस्कोर_ड

4

उपरोक्त परिभाषा देखें जहां यह बताता है कि किसी कॉलबैक फ़ंक्शन को किसी अन्य फ़ंक्शन से पास किया जाता है और कुछ बिंदु पर इसे कहा जाता है।

C ++ में कॉलबैक फ़ंक्शंस के लिए एक क्लास विधि को कॉल करना वांछनीय है। जब आप ऐसा करते हैं तो आपके पास सदस्य डेटा तक पहुंच होती है। यदि आप कॉलबैक को परिभाषित करने के C तरीके का उपयोग करते हैं, तो आपको इसे स्थिर सदस्य फ़ंक्शन पर इंगित करना होगा। यह बहुत वांछनीय नहीं है।

यहाँ आप C ++ में कॉलबैक का उपयोग कैसे कर सकते हैं। 4 फाइलें मान लें। प्रत्येक वर्ग के लिए .CPP / .H फ़ाइलों की एक जोड़ी। क्लास सी 1 एक ऐसी विधि वाला वर्ग है जिसे हम कॉलबैक करना चाहते हैं। C2 C1 की विधि पर वापस कॉल करता है। इस उदाहरण में कॉलबैक फ़ंक्शन 1 पैरामीटर लेता है जो मैंने पाठकों के लिए जोड़ा था। उदाहरण किसी भी ऑब्जेक्ट को त्वरित और उपयोग किए जाने को नहीं दर्शाता है। इस कार्यान्वयन के लिए एक उपयोग मामला तब होता है जब आपके पास एक वर्ग होता है जो अस्थायी स्थान में डेटा को पढ़ता है और संग्रहीत करता है और दूसरा जो डेटा को संसाधित करता है। कॉलबैक फ़ंक्शन के साथ, डेटा की प्रत्येक पंक्ति के लिए कॉलबैक पढ़ सकता है और फिर उसे प्रोसेस कर सकता है। यह तकनीक आवश्यक अस्थायी स्थान के ओवरहेड को काट देती है। यह SQL क्वेरीज़ के लिए विशेष रूप से उपयोगी है जो बड़ी मात्रा में डेटा लौटाता है, जिसे बाद में संसाधित करना पड़ता है।

/////////////////////////////////////////////////////////////////////
// C1 H file

class C1
{
    public:
    C1() {};
    ~C1() {};
    void CALLBACK F1(int i);
};

/////////////////////////////////////////////////////////////////////
// C1 CPP file

void CALLBACK C1::F1(int i)
{
// Do stuff with C1, its methods and data, and even do stuff with the passed in parameter
}

/////////////////////////////////////////////////////////////////////
// C2 H File

class C1; // Forward declaration

class C2
{
    typedef void (CALLBACK C1::* pfnCallBack)(int i);
public:
    C2() {};
    ~C2() {};

    void Fn(C1 * pThat,pfnCallBack pFn);
};

/////////////////////////////////////////////////////////////////////
// C2 CPP File

void C2::Fn(C1 * pThat,pfnCallBack pFn)
{
    // Call a non-static method in C1
    int i = 1;
    (pThat->*pFn)(i);
}

0

बूस्ट के सिग्नल 2 आपको जेनेरिक सदस्य फ़ंक्शंस (टेम्प्लेट के बिना!) और थ्रेडसेफ़ तरीके से सदस्यता लेने की अनुमति देता है।

उदाहरण: दस्तावेज़-व्यू सिग्नल का उपयोग लचीली दस्तावेज़-व्यू आर्किटेक्चर को लागू करने के लिए किया जा सकता है। दस्तावेज़ में एक सिग्नल होगा, जिसमें से प्रत्येक दृश्य कनेक्ट हो सकता है। निम्नलिखित दस्तावेज़ वर्ग एक साधारण पाठ दस्तावेज़ को परिभाषित करता है जो कि मुल्टिपल विचारों का समर्थन करता है। ध्यान दें कि यह एक एकल संकेत संग्रहीत करता है जिससे सभी दृश्य जुड़े होंगे।

class Document
{
public:
    typedef boost::signals2::signal<void ()>  signal_t;

public:
    Document()
    {}

    /* Connect a slot to the signal which will be emitted whenever
      text is appended to the document. */
    boost::signals2::connection connect(const signal_t::slot_type &subscriber)
    {
        return m_sig.connect(subscriber);
    }

    void append(const char* s)
    {
        m_text += s;
        m_sig();
    }

    const std::string& getText() const
    {
        return m_text;
    }

private:
    signal_t    m_sig;
    std::string m_text;
};

अगला, हम विचारों को परिभाषित करना शुरू कर सकते हैं। निम्नलिखित TextView वर्ग दस्तावेज़ पाठ का एक सरल दृश्य प्रदान करता है।

class TextView
{
public:
    TextView(Document& doc): m_document(doc)
    {
        m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
    }

    ~TextView()
    {
        m_connection.disconnect();
    }

    void refresh() const
    {
        std::cout << "TextView: " << m_document.getText() << std::endl;
    }
private:
    Document&               m_document;
    boost::signals2::connection  m_connection;
};

0

स्वीकृत उत्तर व्यापक है लेकिन इस प्रश्न से संबंधित है जिसे मैं यहां एक सरल उदाहरण देना चाहता हूं। मेरे पास एक कोड था जिसे मैंने इसे बहुत पहले लिखा था। मैं इन-ऑर्डर तरीके से एक पेड़ को छोड़ना चाहता था (बाएं-नोड फिर रूट-नोड फिर राइट-नोड) और जब भी मैं एक नोड पर पहुंचता हूं तो मैं एक मनमाना फ़ंक्शन कॉल करने में सक्षम होना चाहता था ताकि यह सब कुछ कर सके।

void inorder_traversal(Node *p, void *out, void (*callback)(Node *in, void *out))
{
    if (p == NULL)
        return;
    inorder_traversal(p->left, out, callback);
    callback(p, out); // call callback function like this.
    inorder_traversal(p->right, out, callback);
}


// Function like bellow can be used in callback of inorder_traversal.
void foo(Node *t, void *out = NULL)
{
    // You can just leave the out variable and working with specific node of tree. like bellow.
    // cout << t->item;
    // Or
    // You can assign value to out variable like below
    // Mention that the type of out is void * so that you must firstly cast it to your proper out.
    *((int *)out) += 1;
}
// This function use inorder_travesal function to count the number of nodes existing in the tree.
void number_nodes(Node *t)
{
    int sum = 0;
    inorder_traversal(t, &sum, foo);
    cout << sum;
}

 int main()
{

    Node *root = NULL;
    // What These functions perform is inserting an integer into a Tree data-structure.
    root = insert_tree(root, 6);
    root = insert_tree(root, 3);
    root = insert_tree(root, 8);
    root = insert_tree(root, 7);
    root = insert_tree(root, 9);
    root = insert_tree(root, 10);
    number_nodes(root);
}

1
यह सवाल का जवाब कैसे देता है?
राजन शर्मा

आप जानते हैं कि स्वीकृत उत्तर सही और व्यापक है और मुझे लगता है कि आम तौर पर कहने के लिए अधिक बात नहीं है। लेकिन मैं कॉलबैक फ़ंक्शन के मेरे उपयोग का एक उदाहरण पोस्ट करता हूं।
एहसान अहमदी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.