সি ++ এ কলব্যাক ফাংশনটি প্রয়োগ করার সময়, আমি এখনও সি-স্টাইলের ফাংশন পয়েন্টার ব্যবহার করব:
void (*callbackFunc)(int);
বা আমার স্ট্যান্ড :: ফাংশনটি ব্যবহার করা উচিত:
std::function< void(int) > callbackFunc;
সি ++ এ কলব্যাক ফাংশনটি প্রয়োগ করার সময়, আমি এখনও সি-স্টাইলের ফাংশন পয়েন্টার ব্যবহার করব:
void (*callbackFunc)(int);
বা আমার স্ট্যান্ড :: ফাংশনটি ব্যবহার করা উচিত:
std::function< void(int) > callbackFunc;
উত্তর:
সংক্ষেপে,std::function
আপনার না করার কারণ না থাকলে ব্যবহার করুন।
ফাংশন পয়েন্টারগুলির কিছু প্রসঙ্গ ক্যাপচার করতে না পারার অসুবিধা রয়েছে । আপনি উদাহরণস্বরূপ একটি কলব্যাক হিসাবে ল্যাম্বডা ফাংশনটি পাস করতে সক্ষম হবেন না যা কিছু প্রসঙ্গের ভেরিয়েবলকে ক্যাপচার করে (তবে এটি কোনওটি ক্যাপচার না করলে এটি কাজ করবে)। কোনও অবজেক্টের সদস্যকে পরিবর্তনশীল (যেমন অ-স্থিতিশীল) কল করাও সম্ভব নয়, যেহেতু বস্তু ( this
-পয়েন্টার) ক্যাপচার করা দরকার। (1)
std::function
(যেহেতু সি ++ 11) মূলত কোনও ফাংশন সংরক্ষণ করা হয় (এটি পাস করার পরে এটি সংরক্ষণ করার প্রয়োজন হয় না)। সুতরাং আপনি যদি কোনও সদস্যের পরিবর্তনশীল হিসাবে উদাহরণস্বরূপ কলব্যাক সঞ্চয় করতে চান তবে এটি সম্ভবত আপনার সেরা পছন্দ। তবে আপনি যদি এটি সংরক্ষণ না করেন তবে এটি একটি "প্রথম পছন্দ" যদিও এটি ডাকা হওয়ার সময় কিছু (খুব ছোট) ওভারহেড চালু করার অসুবিধা রয়েছে (সুতরাং খুব কার্য সম্পাদন-সমালোচনামূলক পরিস্থিতিতে এটি সমস্যা হতে পারে তবে বেশিরভাগ ক্ষেত্রে) এটা করা উচিত নয়)। এটি অত্যন্ত "সর্বজনীন": আপনি যদি সামঞ্জস্যপূর্ণ এবং পঠনযোগ্য কোড সম্পর্কে প্রচুর যত্ন নেন এবং পাশাপাশি আপনার প্রতিটি পছন্দ (যেমন এটি সহজ রাখতে চান) সম্পর্কে চিন্তা করতে না চান তবে std::function
আপনার চারপাশের প্রতিটি ফাংশনের জন্য ব্যবহার করুন ।
তৃতীয় বিকল্পের বিষয়ে চিন্তা করুন: আপনি যদি একটি ছোট ফাংশন প্রয়োগ করতে চলেছেন যা প্রদত্ত কলব্যাক ফাংশনটির মাধ্যমে কিছু রিপোর্ট করে, একটি টেম্পলেট প্যারামিটার বিবেচনা করুন , যা কোনও কলযোগ্য বস্তু , যেমন একটি ফাংশন পয়েন্টার, একটি ফান্টেক্টর, ল্যাম্বদা হতে পারে , a std::function
, ... এখানে ড্রব্যাকটি হ'ল আপনার (বাহ্যিক) ফাংশনটি একটি টেম্পলেট হয়ে যায় এবং তাই শিরোনামে প্রয়োগ করা দরকার। অন্যদিকে আপনি সুবিধাটি পান যে কলব্যাকের সাথে কলটি ইনলাইন করা যায়, কারণ আপনার (বাহ্যিক) ফাংশনের ক্লায়েন্ট কোড কলকে "কল্পনা করে" কল দেয় যে সঠিক টাইপের তথ্য উপলব্ধ।
টেমপ্লেট প্যারামিটার সহ সংস্করণের উদাহরণ ( প্রি-সি ++ এর &
পরিবর্তে লিখুন &&
):
template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}
আপনি যেমন নীচের টেবিলটিতে দেখতে পাচ্ছেন, তাদের সবারই রয়েছে তাদের সুবিধা এবং অসুবিধা:
+-------------------+--------------+---------------+----------------+
| | function ptr | std::function | template param |
+===================+==============+===============+================+
| can capture | no(1) | yes | yes |
| context variables | | | |
+-------------------+--------------+---------------+----------------+
| no call overhead | yes | no | yes |
| (see comments) | | | |
+-------------------+--------------+---------------+----------------+
| can be inlined | no | no | yes |
| (see comments) | | | |
+-------------------+--------------+---------------+----------------+
| can be stored | yes | yes | no(2) |
| in class member | | | |
+-------------------+--------------+---------------+----------------+
| can be implemented| yes | yes | no |
| outside of header | | | |
+-------------------+--------------+---------------+----------------+
| supported without | yes | no(3) | yes |
| C++11 standard | | | |
+-------------------+--------------+---------------+----------------+
| nicely readable | no | yes | (yes) |
| (my opinion) | (ugly type) | | |
+-------------------+--------------+---------------+----------------+
(1) এই সীমাবদ্ধতা কাটিয়ে ওঠার জন্য ওয়ার্কআরউন্ডগুলি বিদ্যমান রয়েছে, উদাহরণস্বরূপ আপনার (বাহ্যিক) ফাংশনে আরও পরামিতি হিসাবে অতিরিক্ত ডেটা পাস করা: myFunction(..., callback, data)
কল করবে callback(data)
। এটি সি-স্টাইলের "আর্গুমেন্টগুলির সাথে কলব্যাক", যা সি ++ এ সম্ভব (এবং যেভাবে ডাব্লুআইএন 32 এপিআইতে ব্যবহৃত হয়েছিল) তবে তা এড়ানো উচিত কারণ আমাদের সি ++ তে আরও ভাল বিকল্প রয়েছে।
(২) যদি না আমরা কোনও ক্লাস টেম্পলেট, অর্থাৎ যে ক্লাসে আপনি ফাংশনটি সঞ্চয় করে থাকি সে সম্পর্কে কথা বলি। তবে এর অর্থ হ'ল ক্লায়েন্টের পক্ষ থেকে ফাংশনের ধরণটি সেই বস্তুর ধরণের সিদ্ধান্ত নেয় যা কলব্যাক সংরক্ষণ করে, যা প্রকৃত ব্যবহারের ক্ষেত্রে প্রায়শই কোনও বিকল্প নয়।
(3) প্রাক-সি ++ 11 এর জন্য ব্যবহার করুন boost::function
std::function
করতে পারেন যা আপনাকে তাৎক্ষণিকভাবে বাইরে সংরক্ষণ করার প্রয়োজন হলে টাইপ-মুছে ফেলাতে রূপান্তর করতে পারেন) প্রসঙ্গ বলা হয়)।
void (*callbackFunc)(int);
কোনও সি স্টাইলের কলব্যাক ফাংশন হতে পারে তবে এটি দরিদ্র ডিজাইনের একটি মারাত্মকভাবে ব্যবহারযোগ্য।
একটি ভাল ডিজাইন করা সি স্টাইলের কলব্যাক দেখতে দেখতে void (*callbackFunc)(void*, int);
- এতে কোডটির void*
অনুমতি দেওয়া আছে যা কলব্যাকটি ফাংশন ছাড়িয়ে রাজ্য বজায় রাখতে পারে। এটি না করা কলকারীকে বিশ্বব্যাপী রাষ্ট্র সঞ্চয় করতে বাধ্য করে, যা অসম্পূর্ণ।
std::function< int(int) >
int(*)(void*, int)
বেশিরভাগ বাস্তবায়নে ডাকে চেয়ে কিছুটা ব্যয়বহুল হয়ে যায়। কিছু সংকলকগুলির জন্য ইনলাইন করা এটি আরও শক্ত। এমন std::function
ক্লোন বাস্তবায়ন রয়েছে যা প্রতিদ্বন্দ্বী ফাংশন পয়েন্টার আহ্বান ওভারহেডগুলি ('দ্রুততম সম্ভাব্য প্রতিনিধি' ইত্যাদি দেখুন) যা লাইব্রেরিতে প্রবেশ করতে পারে।
এখন, কলব্যাক সিস্টেমের ক্লায়েন্টদের প্রায়শই রিসোর্স সেট আপ করতে হবে এবং কলব্যাক তৈরি এবং সরিয়ে ফেলা হওয়ার পরে সেগুলি নিষ্পত্তি করতে হবে এবং কলব্যাকের জীবনকাল সম্পর্কে সচেতন হতে হবে। void(*callback)(void*, int)
এটি সরবরাহ করে না
কখনও কখনও কোড স্ট্রাকচারের মাধ্যমে (কলব্যাকের আয়ু সীমিত থাকে) বা অন্যান্য প্রক্রিয়াগুলির মাধ্যমে (অনিবন্ধিত কলব্যাক এবং এর মতো) এর মাধ্যমে উপলব্ধ।
std::function
সীমিত জীবনকাল পরিচালনার জন্য একটি উপায় সরবরাহ করে (ভুলে গেলে বস্তুর শেষ কপিটি চলে যায়)।
সাধারণভাবে, আমি std::function
যদি না পারফরম্যান্স উদ্বেগ প্রকাশ না করি তবে আমি ব্যবহার করব । যদি তারা তা করে থাকে তবে আমি প্রথমে কাঠামোগত পরিবর্তনগুলি সন্ধান করব (প্রতি পিক্সেল কলব্যাকের পরিবর্তে, আপনি যে ল্যাম্বডা আমাকে পাস করেছেন তার উপর ভিত্তি করে স্ক্যানলাইন প্রসেসর তৈরি করার বিষয়ে কীভাবে? যা ফাংশন-কল ওভারহেডকে তুচ্ছ স্তরে হ্রাস করার জন্য যথেষ্ট হওয়া উচিত) )। তারপরে, যদি এটি অব্যাহত থাকে, আমি delegate
দ্রুত ভিত্তিক সম্ভব প্রতিনিধিদের উপর ভিত্তি করে লিখতাম এবং দেখি যে পারফরম্যান্স সমস্যাটি দূরে যায় কিনা।
আমি সাধারণত লিগ্যাসি এপিআই, বা বিভিন্ন সংকলক উত্পন্ন কোডের মধ্যে যোগাযোগের জন্য সি ইন্টারফেস তৈরি করার জন্য কেবল ফাংশন পয়েন্টার ব্যবহার করব। আমি যখন লাফ টেবিলগুলি প্রয়োগ করি, মুছে ফেলা ইত্যাদি প্রযোজ্য তখন আমি সেগুলি অভ্যন্তরীণ বাস্তবায়নের বিবরণ হিসাবেও ব্যবহার করেছি: যখন আমি উভয়ই উত্পাদন এবং গ্রাস করছি এবং কোনও ক্লায়েন্ট কোড ব্যবহার করার জন্য এটি বাহ্যিকভাবে প্রকাশ করছি না, এবং ফাংশন পয়েন্টারগুলি আমার প্রয়োজনীয় সমস্তই করছে ।
নোট করুন যে আপনি উপযুক্ত কলব্যাক লাইফটাইম ম্যানেজমেন্ট অবকাঠামো রয়েছে তা ধরে নিয়ে এমন স্টাইল কলব্যাককে std::function<int(int)>
রূপান্তর করে এমন মোড়ক লিখতে পারেন int(void*,int)
। সুতরাং যে কোনও সি-স্টাইলের কলব্যাক লাইফটাইম ম্যানেজমেন্ট সিস্টেমের জন্য ধূমপান পরীক্ষা হিসাবে, আমি নিশ্চিত করব যে কোনও std::function
কাজকে যুক্তিসঙ্গতভাবে গুটিয়ে রাখা উচিত।
void*
থেকে এলো? আপনি কেন ফাংশন ছাড়িয়ে রাষ্ট্র বজায় রাখতে চান? একটি ফাংশনটিতে তার প্রয়োজনীয় সমস্ত কোড, সমস্ত কার্যকারিতা থাকা উচিত, আপনি কেবল এটি পছন্দসই যুক্তিগুলি পাস করে কিছু পরিবর্তন এবং ফিরিয়ে আনতে পারেন। আপনার যদি কিছু বাহ্যিক অবস্থার প্রয়োজন হয় তবে কোনও ফাংশনপিআর বা কলব্যাক কেন সেই লাগেজ বহন করবে? আমি মনে করি যে কলব্যাক অহেতুক জটিল।
this
। এটি কারণ যে কোনও সদস্য ফাংশনের জন্য ডাকা হচ্ছে তার অ্যাকাউন্টে অ্যাকাউন্ট করতে হয়, তাই আমাদের this
কীসের জন্য পয়েন্টারটির প্রয়োজনবোধ করার প্রয়োজন ? আমি যদি ভুল হয় তবে আপনি কি আমাকে একটি লিঙ্ক দিতে পারেন যেখানে আমি এই বিষয়ে আরও তথ্য পেতে পারি, কারণ আমি এটি সম্পর্কে খুব বেশি কিছু পাই না। আগাম ধন্যবাদ.
void*
রানটাইম রাজ্যের সংক্রমণকে অনুমতি দেয়। একটি void*
এবং একটি void*
আর্গুমেন্ট সহ একটি ফাংশন পয়েন্টার একটি বস্তুর একটি সদস্য ফাংশন কল অনুকরণ করতে পারে। দুঃখিত, আমি এমন একটি সংস্থান সম্পর্কে জানি না যা "সি কলব্যাক মেকানিজম 101 ডিজাইনিং" এর মধ্য দিয়ে চলে।
this
। যে আমি বোঝানো কি. ঠিক আছে, যাইহোক ধন্যবাদ।
std::function
যথেচ্ছ কলযোগ্য বস্তু সঞ্চয় করতে ব্যবহার করুন । এটি ব্যবহারকারীকে কলব্যাকের জন্য প্রাসঙ্গিক যা প্রয়োজন তা সরবরাহ করতে দেয়; একটি সরল ফাংশন পয়েন্টার না।
যদি আপনার কোনও কারণে প্লেইন ফাংশন পয়েন্টার ব্যবহার করার প্রয়োজন হয় (সম্ভবত আপনি সি-সামঞ্জস্যপূর্ণ এপিআই চান) তবে আপনার পক্ষে void * user_context
যুক্তি যুক্ত করা উচিত যাতে এটির অ্যাক্সেসের জন্য কমপক্ষে সম্ভব (অসুবিধে হলেও) যা সরাসরি প্রবেশ করা হয়নি access ফাংশন।
এড়ানোর একমাত্র কারণ std::function
হ'ল লেগ্যাসি সংকলকগুলির সমর্থন যা এই টেমপ্লেটের জন্য সমর্থন সমর্থন করে না, যা সি ++ 11 এ চালু করা হয়েছে।
প্রাক-সি ++ ১১ টি ভাষা সমর্থন করা যদি প্রয়োজন হয় না, ব্যবহার std::function
করে আপনার কলারকে কলব্যাক বাস্তবায়নে আরও পছন্দ দেয়, এটি "সাধারণ" ফাংশন পয়েন্টারের তুলনায় আরও ভাল বিকল্প হিসাবে তৈরি করে। এটি আপনার এপিআই-র ব্যবহারকারীদের আরও পছন্দ দেয়, যখন আপনার কোডটির জন্য কলব্যাক সম্পাদন করে তাদের বাস্তবায়নের সুনির্দিষ্ট বিবরণ বের করে।