সি ++ এ কলব্যাক ফাংশন


303

সি ++ এ আপনি কখন এবং কীভাবে কলব্যাক ফাংশন ব্যবহার করবেন?

সম্পাদনা:
কলব্যাক ফাংশন লেখার জন্য আমি একটি সাধারণ উদাহরণ দেখতে চাই।


[এটি] ( thispointer.com/… ) কলব্যাক ফাংশন সম্পর্কে ধারণাটি খুব ভাল এবং সহজে বোঝার জন্য ব্যাখ্যা করে।
অনুরাগ সিং

উত্তর:


449

দ্রষ্টব্য: বেশিরভাগ উত্তরগুলি ফাংশন পয়েন্টারগুলিকে আচ্ছাদন করে যা সি ++ এ "কলব্যাক" যুক্তি অর্জন করার এক সম্ভাবনা, তবে আজ পর্যন্ত আমার পক্ষে সর্বাধিক অনুকূল নয়।

কলব্যাকগুলি কী (?) এবং কেন সেগুলি ব্যবহার করতে হবে (!)

একটি কলব্যাক একটি কলযোগ্য (আরও নীচে দেখুন) কোনও শ্রেণি বা ফাংশন দ্বারা গৃহীত, সেই কলব্যাকের উপর নির্ভর করে বর্তমান যুক্তিকে কাস্টমাইজ করতে ব্যবহৃত হয়।

কলব্যাকগুলি ব্যবহার করার একটি কারণ জেনেরিক কোড লেখা যা ডাকা ফাংশনে যুক্তি থেকে স্বতন্ত্র এবং বিভিন্ন কলব্যাকের সাথে পুনরায় ব্যবহার করা যেতে পারে।

স্ট্যান্ডার্ড অ্যালগোরিদম লাইব্রেরির অনেকগুলি কার্য <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) এ কল করার আচরণ পরিবর্তন করুন ।

সি ++ (11) এ কলযোগ্য কী কী ?

দেখুন Callable: সি ++ ধারণা আরো একটি প্রথাগত বর্ণনার জন্য cppreference উপর।

কলব্যাক কার্যকারিতা সি ++ (১১) এ বেশ কয়েকটি উপায়ে উপলব্ধি করা যায় যেহেতু বেশ কয়েকটি বিভিন্ন জিনিস কলযোগ্য বলে পরিণত হয় * :

  • ফাংশন পয়েন্টার (সদস্য ফাংশন পয়েন্টার সহ)
  • std::function বস্তু
  • লাম্বদা এক্সপ্রেশন
  • বাইন্ড এক্সপ্রেশন
  • ফাংশন অবজেক্টস (অতিরিক্ত লোড ফাংশন কল অপারেটর সহ ক্লাস operator())

* দ্রষ্টব্য: ডেটা সদস্যদের পয়েন্টার পাশাপাশি কলযোগ্য তবে কোনও ফাংশনও বলা হয় না।

কলব্যাকগুলি বিস্তারিত লেখার কয়েকটি গুরুত্বপূর্ণ উপায়

  • X.1 এই পোস্টে একটি কলব্যাক "লিখন" এর অর্থ কলব্যাক প্রকারটি ঘোষণা এবং নামকরণের বাক্য গঠন means
  • এক্স 2 "কলিং" কলব্যাক সেই শব্দগুলিকে কল করার সিনট্যাক্সকে বোঝায়।
  • এক্স.3 "কলব্যাক" ব্যবহার করে একটি কলব্যাক ব্যবহার করে কোনও ফাংশনে আর্গুমেন্টগুলি পাস করার সময় বাক্য গঠন বোঝানো হয়।

দ্রষ্টব্য: সি ++ ১ of এর মতো একটি কল 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);

১.২ কলব্যাক কল স্বরলিপি

কল স্বরলিপি সাধারণ ফাংশন কল বাক্য গঠন অনুসরণ করে:

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};

২. সদস্য ফাংশন নির্দেশক

সদস্য ফাংশনটির কোনও পয়েন্টার (কিছু শ্রেণির C) একটি বিশেষ ধরণের (এবং আরও জটিল) ফাংশন পয়েন্টার যা Cপরিচালনা করতে টাইপের একটি অবজেক্টের প্রয়োজন ।

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

২.১ সদস্য ফাংশন / টাইপ স্বরলিপি পয়েন্টার লিখন

কিছু শ্রেণীর জন্য সদস্য ফাংশন প্রকারের একটি পয়েন্টারের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);

২.২ কলব্যাক কল স্বরলিপি

অবহিত পয়েন্টারে সদস্য অ্যাক্সেস অপারেশনগুলি ব্যবহার করে 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); 
}

২.৩ কলব্যাক স্বরলিপি এবং সামঞ্জস্যপূর্ণ প্রকারের ব্যবহার

ক্লাসের সদস্য ফাংশন পয়েন্টার গ্রহণকারী একটি কলব্যাক ফাংশনকে ক্লাসের 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;

৩.২ কলব্যাক কল স্বরলিপি

ক্লাসটি সংজ্ঞায়িত 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
}

৩.৩ কলব্যাক স্বরলিপি এবং সামঞ্জস্যপূর্ণ প্রকারের ব্যবহার

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) )

ব্যবহার করা যেতে পারে.

3.3.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)

3.3.3 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]);
  }
}

এই ফাংশনটিতে পুরোপুরি আরও বেশি উপযোগিতা দেয় কারণ (৩.৩ দেখুন) আমাদের এটি ব্যবহারের আরও বেশি সম্ভাবনা রয়েছে:

// 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অবজেক্ট ব্যবহারের চেয়ে আরও সাধারণ হতে পারে ।

নোট করুন যে টেমপ্লেটগুলি একটি সংকলন-সময় বৈশিষ্ট্য এবং সংকলন-কাল পলিমারফিজমের জন্য একটি নকশার সরঞ্জাম। কলব্যাকের মাধ্যমে যদি রানটাইম গতিশীল আচরণ অর্জন করতে হয়, টেমপ্লেটগুলি সহায়তা করবে তবে তারা রানটাইম গতিশীলতা প্ররোচিত করবে না।

৪.১ রচনা (স্বরলিপিগুলি টাইপ করুন) এবং কল্পনাযুক্ত কলব্যাকগুলি কল করা

সাধারণকরণ অর্থাত 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;
}

৪.২ টেম্পলেটড কলব্যাকস এবং সামঞ্জস্যপূর্ণ প্রকারগুলি ব্যবহার করে উদাহরণ

টেম্প্লেটেড std::functionকলব্যাক পদ্ধতির জন্য সামঞ্জস্যপূর্ণ প্রকারগুলি stdf_transform_every_int_templউপরে বর্ণিত প্রকারের সাথে সমান (দেখুন ৩.৪ দেখুন)।

টেম্প্লেটেড সংস্করণটি ব্যবহার করে তবে ব্যবহৃত কলব্যাকের স্বাক্ষরটি কিছুটা বদলে যেতে পারে:

// 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
@ বুজিএ জামার: যদি আপনি খেয়াল না করেন: উত্তরের দুটি অংশ রয়েছে। 1. একটি ছোট উদাহরণ সহ "কলব্যাকস" এর সাধারণ ব্যাখ্যা। ২. বিভিন্ন কলযোগ্যগুলির একটি বিস্তৃত তালিকা এবং কলব্যাকগুলি ব্যবহার করে কোড লেখার উপায়। আপনি বিশদে খোঁড়াখুঁড়ি বা পুরো উত্তরটি না পড়তে আপনাকে স্বাগত জানাই তবে কেবল আপনি বিশদ দৃষ্টিভঙ্গি চান না, উত্তরটি অকার্যকর বা "নির্মমভাবে অনুলিপি করা হয়েছে" এমনটি নয়। বিষয়টি হ'ল "সি ++ কলব্যাকস"। এমনকি অংশ 1 ওপির জন্য ঠিক আছে, অন্যরা অংশ 2 টি দরকারী বলে মনে করতে পারে। -১ এর পরিবর্তে প্রথম অংশের জন্য কোনও তথ্যের অভাব বা গঠনমূলক সমালোচনার বিন্দু নির্দ্বিধায় মনে করুন।
পিক্সেল কেমিস্ট

1
অংশ 1 প্রাথমিকভাবে বন্ধুত্বপূর্ণ এবং যথেষ্ট পরিষ্কার নয়। এটি আমাকে কিছু শেখার ব্যবস্থা করে নি বলে আমি আরও গঠনমূলক হতে পারি না। এবং অংশ 2, অনুরোধ করা হয়নি, পৃষ্ঠাটি প্লাবিত করা এবং প্রশ্নটির বাইরে থাকা সত্ত্বেও আপনি এটির ভান করেছেন যদিও এটি সাধারণত ডেডিকেটেড ডকুমেন্টেশনে পাওয়া যায় যেখানে এই জাতীয় বিস্তারিত তথ্য প্রথম স্থানে সন্ধান করা হয়। আমি অবশ্যই ডাউনটাতে রাখি। একটি একক ভোট একটি ব্যক্তিগত মতামত উপস্থাপন করে তাই দয়া করে এটি গ্রহণ করুন এবং সম্মান করুন।
Bogey Jammer

24
@ বুজিজেমার আমি প্রোগ্রামিংয়ে নতুন নই তবে আমি "আধুনিক সি ++" তে নতুন। এই উত্তরটি আমাকে কলব্যাকগুলি বিশেষত সি ++ তে কী ভূমিকা পালন করবে সে সম্পর্কে যুক্তিযুক্ত হওয়ার সঠিক প্রসঙ্গটি দেয়। ওপি একাধিক উদাহরণের জন্য না চেয়ে থাকতে পারে, তবে এটি কোনও প্রকারের সম্ভাব্য সমাধানগুলি গণনা করার জন্য বোকাদের জগতকে শিক্ষিত করার এক সর্বশেষে অন্বেষণে এসও-র প্রথাগত। যদি এটি একটি বইয়ের মতো খুব বেশি পড়েন তবে আমি যে পরামর্শ দিতে পারি তার মধ্যে কয়েকটি পড়ার মাধ্যমে কিছুটা অনুশীলন করা ।
ডিসো

int b = foobar(a, foo); // call foobar with pointer to foo as callback, এটা ঠিক টাইপো? fooএএফআইকে কাজ করার জন্য এটির জন্য একটি পয়েন্টার হওয়া উচিত।
konoufo

@ কনৌফো: [conv.func]সি ++ ১১ স্ট্যান্ডার্ডের মধ্যে বলেছেন: " ফাংশন টাইপের টিয়ের একটি মূল্যমান" পয়েন্টার টি-তে "রূপান্তরিত হতে পারে type ফলাফলটি ফাংশনটির একটি পয়েন্টার "" এটি একটি স্ট্যান্ডার্ড রূপান্তর এবং এর মতো স্পষ্টভাবে ঘটে। কেউ (অবশ্যই) এখানে ফাংশন পয়েন্টার ব্যবহার করতে পারে।
পিক্সেল কেমিস্ট

160

কলব্যাকগুলি করার সি পদ্ধতিও রয়েছে: ফাংশন পয়েন্টার

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

আমি 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<> সি ++ কলব্যাক লেখার "আধুনিক" উপায়।


1
আগ্রহের বাইরে, কোন বইয়ের এসএম এই উদাহরণ দেয়? চিয়ার্স :)
সাম-ডব্লু

5
আমি জানি এটি পুরানো, তবে যেহেতু আমি প্রায় এটি করা শুরু করেছি এবং এটি আমার সেটআপ (মিংডাব্লু) এ কাজ না করে শেষ হয়েছে, যদি আপনি জিসিসি সংস্করণ <৪.x ব্যবহার করেন তবে এই পদ্ধতিটি সমর্থিত নয়। আমি যে নির্ভরশীলতাগুলি ব্যবহার করছি তার মধ্যে কিছুটি জিসিসি সংস্করণ> = ৪.০.১ তে অনেক কাজ না করে সংকলন করবে না, তাই আমি ভাল পুরানো ফ্যাশনযুক্ত সি স্টাইলের কলব্যাকগুলি ব্যবহার করে আটকেছি, যা ঠিক কাজ করে।
ওজবারি

38

একটি কলব্যাক ফাংশন একটি পদ্ধতি যে একটি রুটিন মধ্যে পাস করা হয়, এবং বলা রুটিন যা এটি পাস করা হয়েছে কিছু পয়েন্ট।

এটি পুনরায় ব্যবহারযোগ্য সফ্টওয়্যার তৈরির জন্য খুব দরকারী। উদাহরণস্বরূপ, অনেক অপারেটিং সিস্টেম এপিআই (যেমন উইন্ডোজ এপিআই) কলব্যাকগুলি ভারী ব্যবহার করে।

উদাহরণস্বরূপ, আপনি যদি কোনও ফোল্ডারে ফাইলগুলি নিয়ে কাজ করতে চান - আপনি নিজের রুটিন সহ একটি API ফাংশন কল করতে পারেন এবং নির্দিষ্ট ফোল্ডারে প্রতি রুটিন একবার ফাইলের জন্য চলে run এটি API কে খুব নমনীয় হতে দেয়।


63
এই উত্তরটি সত্যিই গড় প্রোগ্রামারকে এমন কিছু বলে না যা সে জানত না। অন্যান্য অনেক ভাষার সাথে পরিচিত হওয়ার সময় আমি সি ++ শিখছি। সাধারণত কলব্যাক আমার সম্পর্কে উদ্বেগ প্রকাশ করে না।
টোমা জাটো - মনিকা

17

গৃহীত উত্তরটি খুব দরকারী এবং বেশ ব্যাপক। তবে ওপি জানিয়েছে

আমি একটি কলব্যাক ফাংশন লিখতে একটি সাধারণ উদাহরণ দেখতে চাই ।

সুতরাং আপনি এখানে যান, সি ++ 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&)>এটি ফাংশনটির ইনপুট আর্গুমেন্ট হিসাবে এই জাতীয় ফাংশনকে বর্ণনা করে যা এটি শুরু করবে।


উপরের সমস্ত উত্তরগুলির মধ্যে, এইটি আমাকে কলব্যাকগুলি কী এবং কীভাবে সেগুলি ব্যবহার করতে হয় তা বুঝতে সক্ষম করে। ধন্যবাদ।
মেহের চরণ সাহাই

এটি শুনে মেহেরচরণসাহাই খুশী হয়েছিলেন :) আপনাকে স্বাগতম are
মিলজেন মিকিক

9

C ++ এ কলব্যাক ফাংশনের সুস্পষ্ট ধারণা নেই। কলব্যাক প্রক্রিয়াগুলি প্রায়শই ফাংশন পয়েন্টার, ফান্টার অবজেক্টস বা কলব্যাক অবজেক্টগুলির মাধ্যমে প্রয়োগ করা হয়। প্রোগ্রামারদের স্পষ্টভাবে কলব্যাক কার্যকারিতা ডিজাইন এবং প্রয়োগ করতে হবে।

প্রতিক্রিয়ার ভিত্তিতে সম্পাদনা করুন:

এই উত্তরটি নেতিবাচক প্রতিক্রিয়া সত্ত্বেও, এটি ভুল নয়। আমি কোথা থেকে আসছি তা বোঝানোর জন্য আরও ভাল কাজ করার চেষ্টা করব।

সি এবং সি ++ এর কলব্যাক ফাংশন বাস্তবায়নের জন্য আপনার প্রয়োজনীয় সমস্ত কিছুই রয়েছে। কলব্যাক ফাংশন বাস্তবায়নের সর্বাধিক সাধারণ এবং তুচ্ছ উপায় হ'ল ফাংশন আর্গুমেন্ট হিসাবে কোনও ফাংশন পয়েন্টারটি পাস করা।

তবে কলব্যাক ফাংশন এবং ফাংশন পয়েন্টার সমার্থক নয়। একটি ফাংশন পয়েন্টার একটি ভাষা প্রক্রিয়া, অন্যদিকে কলব্যাক ফাংশন একটি শব্দার্থক ধারণা। কলব্যাক ফাংশন বাস্তবায়নের একমাত্র উপায় ফাংশন পয়েন্টার নয় - আপনি ফান্টেক্টর এবং এমনকি বাগানের বিভিন্ন ভার্চুয়াল ফাংশনও ব্যবহার করতে পারেন। কোন ফাংশনকে কলব্যাক কল করে তা হ'ল ফাংশনটি সনাক্ত এবং কল করার জন্য ব্যবহৃত প্রক্রিয়া নয়, তবে কলটির প্রসঙ্গ এবং শব্দার্থক। কিছু বলা কলব্যাক ফাংশন বলতে কলিং ফাংশন এবং কল করা নির্দিষ্ট ফাংশনটির মধ্যে স্বাভাবিক বিচ্ছিন্নতার চেয়ে বেশি বোঝায়, কলার এবং কলির মধ্যে একটি আলগা ধারণাগত মিলন, কলারের সাথে তার কী নিয়ন্ত্রণ রয়েছে তার স্পষ্ট নিয়ন্ত্রণ রয়েছে।

উদাহরণস্বরূপ, আইফোর্মেটপ্রভাইডারের জন্য .NET ডকুমেন্টেশন বলে যে "গেটফর্ম্যাটটি একটি কলব্যাক পদ্ধতি" , যদিও এটি কেবলমাত্র রান-অফ-দ্য মিল-ইন্টারফেস পদ্ধতি। আমি মনে করি না যে কেউ যুক্তি দিবে যে সমস্ত ভার্চুয়াল পদ্ধতি কলগুলি কলব্যাক ফাংশন। গেটফর্ম্যাট কী কলব্যাক পদ্ধতিতে পরিণত হয় তা কীভাবে পাস হয় বা আহ্বান করা হয় তার মেকানিক্স নয়, তবে কলরটি বাছাইয়ের শব্দার্থকে কোন বস্তুর গেটফর্ম্যাট পদ্ধতি বলা হবে।

কিছু ভাষায় স্পষ্ট কলব্যাক শব্দার্থক বৈশিষ্ট্য অন্তর্ভুক্ত থাকে, সাধারণত ইভেন্ট এবং ইভেন্ট হ্যান্ডলিং সম্পর্কিত। উদাহরণস্বরূপ, সি # এর সিনট্যাক্স এবং শব্দার্থবিজ্ঞানের সাথে ইভেন্টের টাইপ রয়েছে যা স্পষ্টভাবে কলব্যাক্স ধারণার চারপাশে নকশা করা হয়েছে। ভিজ্যুয়াল বেসিকের হ্যান্ডলস ক্লজ রয়েছে, যা ডেলিগেটস বা ফাংশন পয়েন্টারগুলির ধারণাকে দূরে রেখে স্পষ্টভাবে একটি পদ্ধতিকে কলব্যাক ফাংশন হিসাবে ঘোষণা করে। এই ক্ষেত্রে, কলব্যাকের অর্থগত ধারণাটি ভাষাতেই সংহত করা হয়।

অন্যদিকে সি এবং সি ++, কলব্যাক ফাংশনগুলির সিনেম্যাটিক ধারণাটি প্রায় পরিষ্কারভাবে এম্বেড করে না । প্রক্রিয়াগুলি আছে, সংহত শব্দার্থকগুলি নেই। আপনি কলব্যাক ফাংশনগুলি ঠিকঠাক বাস্তবায়িত করতে পারেন তবে আরও কিছু পরিশীলিত জিনিস পেতে যাতে স্পষ্টত কলব্যাক শব্দার্থাদি অন্তর্ভুক্ত থাকে আপনাকে সি ++ সরবরাহ করে তার উপরে এটি তৈরি করতে হবে যেমন কিউটি তাদের সংকেত এবং স্লট দিয়ে কী করেছিল ।

সংক্ষেপে, সি ++ এর যা আপনার কলব্যাকগুলি বাস্তবায়িত করতে হবে তা প্রায়শই বেশ সহজে এবং তুচ্ছভাবে ফাংশন পয়েন্টার ব্যবহার করে। এটিতে কীওয়ার্ড এবং বৈশিষ্ট্য নেই যা শব্দার্থক কলব্যাকগুলির সাথে নির্দিষ্ট, যেমন উত্থাপন , নির্গমন , হ্যান্ডলস , ইভেন্ট + = ইত্যাদি you 're নিবিড় বোধ করবে


1
ভাগ্যক্রমে আমি এই পৃষ্ঠাগুলি দেখার সময় আমি যে প্রথম উত্তরটি পড়েছিলাম তা ছিল না, অন্যথায় আমি তাত্ক্ষণিকভাবে বাউন্স করতাম!
উবুগনু

6

কলব্যাক ফাংশনগুলি সি স্ট্যান্ডার্ডের অংশ, এটি সি ++ এরও একটি অংশ। তবে আপনি যদি সি ++ নিয়ে কাজ করছেন তবে আমি পরামর্শ দেব এর পরিবর্তে আপনি পর্যবেক্ষক প্যাটার্নটি ব্যবহার করুন: http://en.wikedia.org/wiki/Observer_ Pattern


1
আর্গুমেন্ট হিসাবে পাস করা একটি ফাংশন পয়েন্টারের মাধ্যমে কোনও ফাংশন সম্পাদন করার জন্য কলব্যাক ফাংশনগুলি অগত্যা সমার্থক নয়। কিছু সংজ্ঞা অনুসারে, শব্দটি কলব্যাক ফাংশনটি সবেমাত্র ঘটেছিল এমন কিছু অন্য কোডকে অবহিত করার অতিরিক্ত শব্দার্থ বহন করে, বা সময় এসেছে যে কিছু হওয়া উচিত। সেই দৃষ্টিকোণ থেকে, একটি কলব্যাক ফাংশন সি স্ট্যান্ডার্ডের অংশ নয়, তবে ফাংশন পয়েন্টারগুলি ব্যবহার করে সহজেই প্রয়োগ করা যেতে পারে, যা স্ট্যান্ডার্ডের অংশ।
ড্যারিল 16

3
"সি স্ট্যান্ডার্ডের একটি অংশ, এটি সি ++ এরও একটি অংশ" " এটি একটি সাধারণ ভুল বোঝাবুঝি, তবে তবুও একটি ভুল বোঝাবুঝি
সীমিত প্রায়শ্চিত্ত

আমাকে একমত হতে হবে আমি এটি যেমন আছে তেমনি রেখে দেব, যেহেতু আমি যদি এখন এটি পরিবর্তন করি তবে এটি আরও বিভ্রান্তি সৃষ্টি করবে। আমি বলতে চাইছিলাম যে ফাংশন পয়েন্টার (!) স্ট্যান্ডার্ডের অংশ। এর থেকে আলাদা কিছু বলা - আমি সম্মত - বিভ্রান্তিকর।
অডিওড্রয়েড

কীভাবে কলব্যাক ফাংশনগুলি "সি স্ট্যান্ডার্ডের অংশ"? আমি মনে করি না যে এটি ফাংশনগুলিকে সমর্থন করে এবং ফাংশনগুলিতে পয়েন্টার দেয় তার মানে এটি একটি ভাষা ধারণা হিসাবে কলব্যাকগুলিকে বিশেষভাবে প্রেরণ করে। পাশাপাশি, যেমনটি উল্লেখ করা হয়েছে, এটি সঠিক হলেও সি ++ এর সাথে সরাসরি প্রাসঙ্গিক হবে না। এবং এটি বিশেষত প্রাসঙ্গিক নয় যখন সি ++ (একটি খোঁড়া, খুব বিস্তৃত প্রশ্ন, তবে যাইহোক) -এ কলব্যাকগুলি "কখন এবং কীভাবে" ব্যবহার করতে বলা হয়েছিল, এবং আপনার উত্তর পরিবর্তে কিছু আলাদা করার জন্য লিঙ্ক-কেবলমাত্র উপদেশ।
আন্ডারস্কোর_

4

উপরের সংজ্ঞাটি দেখুন যেখানে এটিতে বলা হয়েছে যে কলব্যাক ফাংশনটি অন্য কোনও ফাংশনে পাঠানো হয়েছে এবং কিছু সময় এটি ডাকা হয়।

সি ++ এ কলব্যাক ফাংশনগুলিকে একটি ক্লাস পদ্ধতিতে কল করা বাঞ্ছনীয়। আপনি যখন এটি করেন আপনার সদস্যের ডেটাতে অ্যাক্সেস থাকে। আপনি যদি কলব্যাক সংজ্ঞায়নের সি উপায় ব্যবহার করেন তবে আপনাকে এটি একটি স্ট্যাটিক সদস্য ফাংশনটির দিকে নির্দেশ করতে হবে। এটি খুব পছন্দসই নয় is

আপনি কিভাবে সি ++ এ কলব্যাকগুলি ব্যবহার করতে পারেন তা এখানে। 4 টি ফাইল ধরুন। প্রতিটি ক্লাসের জন্য একজোড়া .CPP / .H ফাইল। ক্লাস সি 1 এমন একটি ক্লাস যার সাথে আমরা কলব্যাক করতে চাই। সি 2 সি 1 এর পদ্ধতিতে ফিরে কল করে। এই উদাহরণে কলব্যাক ফাংশনটিতে 1 টি প্যারামিটার লাগে যা আমি পাঠকদের জন্য যুক্ত করেছিলাম। উদাহরণটি কোনও বস্তু তাত্ক্ষণিকভাবে ব্যবহৃত এবং ব্যবহৃত হচ্ছে তা দেখায় না। এই প্রয়োগের জন্য একটি ব্যবহারের ক্ষেত্রে হ'ল যখন আপনার কাছে একটি শ্রেণি থাকে যা অস্থায়ী স্থানের মধ্যে ডেটা পড়ে এবং সঞ্চয় করে এবং অন্য পোস্টে ডেটা প্রসেস করে। কলব্যাক ফাংশন সহ, প্রতিটি সারি ডেটা পড়ার জন্য কলব্যাক তারপরে প্রক্রিয়া করতে পারে। এই কৌশলটি অস্থায়ী জায়গার প্রয়োজনীয় ওভারহেড কেটে দেয়। এটি এসকিউএল অনুসন্ধানগুলির জন্য বিশেষত কার্যকর যেগুলি প্রচুর পরিমাণে ডেটা ফেরত দেয় যা পোস্ট-প্রসেস করা উচিত।

/////////////////////////////////////////////////////////////////////
// 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;
};

এরপরে, আমরা মতামত সংজ্ঞায়িত করতে শুরু করতে পারি। নিম্নলিখিত টেক্সটভিউ ক্লাসটি নথির পাঠ্যের একটি সহজ ভিউ সরবরাহ করে।

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
কিভাবে এটি প্রশ্নের উত্তর দেয়?
রাজন শর্মা

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