ফাংশন পয়েন্টার ও কলব্যাকসমূহ (Function Pointers & Callbacks)
প্রোগ্রামেবল রিমোট কন্ট্রোল (The Programmable Remote Control)
বেশিরভাগ সি (C) কোডেই, আপনি সাধারণত যেকোনো ফাংশনকে সরাসরি তাদের নাম ধরেই ডাকেন বা কল (call) করেন: যেমন printf(), strlen(), main()। কিন্তু যদি এমন হতো যে আপনি চাইলে যেকোনো একটি ফাংশনকে একটি ভ্যারিয়েবলের (variable) ভেতর সেভ করে (store) রাখতে পারতেন, সেটিকে অন্য কোনো ফাংশনের (function) কাছে পাস (pass) করে দিতে পারতেন, বা রানটাইমের (runtime) সময় ঠিক কোন ফাংশনটিকে ডাকতে বা কল (call) করতে হবে তা নিজে থেকেই বেছে নিতে পারতেন? তবে তখন কেমন হতো?
আর মূলত ঠিক এই কাজটিই ফাংশন পয়েন্টারগুলো (function pointers) করে থাকে। ঠিক যেমনভাবে একটি সাধারণ পয়েন্টার (regular pointer) কোনো ভ্যারিয়েবলের (variable) ঠিকানা বা অ্যাড্রেসকে (address) ধারণ করে রাখে, তেমনি একটি ফাংশন পয়েন্টারও মূলত একটি ফাংশনের ঠিকানা (address of a function) ধারণ করে রাখে। এটি অনেকটা এমন একটি রিমোট কন্ট্রোলের (remote control) মতো যার প্রতিটি বোতাম আলাদা আলাদা কাজের (action) ঠিকানা (address) সেভ বা ধারণ করে রাখে — এবং আপনি চাইলে যেকোনো সময় এই বোতামগুলোকে রিপ্রোগ্রাম (reprogram) বা নতুনভাবে সেট করে নিতে পারেন।
এর সিনট্যাক্স (The Syntax) (হ্যাঁ, এটি দেখতে বেশ কুৎসিত বা Ugly)
একটি ফাংশন পয়েন্টারের (function pointer) ডিক্লেয়ারেশন (declaration) বা এর দেখতে মূলত কিছুটা এমন হয়:
return_type (*pointer_name)(param_types);
এখানে এই *pointer_name-এর চারপাশের ব্র্যাকেটগুলো বা প্যারেনথেসিসগুলো (parentheses) অনেক বেশি গুরুত্বপূর্ণ (critical)। এগুলো ছাড়া, আপনি হয়তো এমন একটি ফাংশনকে ডিক্লেয়ার করে ফেলবেন যেটি কিনা মূলত একটি পয়েন্টার (pointer) রিটার্ন (returns) করে — আর সেটি কিন্তু সম্পূর্ণ ভিন্ন বা আলাদা একটি জিনিস হয়ে দাঁড়াবে।
বেসিক ফাংশন পয়েন্টার (Basic Function Pointer)
উদ্ধারকারী বা সেভিয়ার হিসেবে typedef (typedef to the Rescue)
ফাংশন পয়েন্টারের (function pointers) এই র সিনট্যাক্সটি (raw syntax) পড়া যে কতটা কঠিন তা আমরা সবাই জানি। এই typedef মূলত আপনাকে এই ফাংশন পয়েন্টার টাইপগুলোর (function pointer type) জন্য একটি সুন্দর, পরিষ্কার এবং রিডেবল নাম (readable name) তৈরি করতে সাহায্য করে। একবার আপনি এর টাইপটিকে (type) ডিফাইন বা সংজ্ঞায়িত (define) করে নিলে, আপনি এর সাহায্যে যেকোনো ভ্যারিয়েবল (variables), ফাংশন প্যারামিটার (function parameters) এবং অ্যারেগুলোকে (arrays) ঠিক এর অন্যান্য সাধারণ টাইপের (other type) মতোই করে ডিক্লেয়ার বা ঘোষণা করতে পারবেন।
ফাংশন পয়েন্টারের জন্য typedef (typedef for Function Pointers)
typedef মূলত এর এই পুরো ব্যাপারটিকে অনেকটাই রিডেবল (readable) বা পাঠঅযোগ্য করে তোলে। তাই ফাংশন পয়েন্টার টাইপগুলোর (function pointer types) জন্য সবসময় এই typedef ব্যবহার করার চেষ্টা করবেন — এতে করে আপনার ভবিষ্যৎ সত্তা (future self) (এবং আপনার টিমের অন্যান্য সবাই) আপনার এই কাজের জন্য বিশেষভাবে ধন্যবাদ দেবে।কলব্যাক — আর্গুমেন্ট হিসেবে ফাংশন পাস করা (Callbacks — Passing Functions as Arguments)
কলব্যাক বা callback হলো এমন একটি ফাংশন (function) যাকে আপনি অন্য আরেকটি ফাংশনে এই বলে পাস (pass) করে দেন যে: "যখন তোমার প্রয়োজন পড়বে তখন তুমি এটিকে ডেকে নিও (Call this when you need to)।" এটি মূলত সি (C) প্রোগ্রামিংয়ের সবচেয়ে শক্তিশালী (powerful) নিদর্শন বা প্যাটার্নগুলোর (patterns) অন্যতম একটি অংশ। স্টান্ডার্ড লাইব্রেরিও (standard library) সব জায়গায় এটির ব্যবহার করে থাকে — যেমন qsort, bsearch, সিগন্যাল হ্যান্ডলার (signal handlers), এবং অন্যান্য অনেক কিছুতেই।
ব্যাপারটিকে কোনো কন্ট্রাক্টর (contractor) বা ঠিকাদারকে ভাড়া করার সময় তাকে একটি ফোন নম্বর (phone number) ধরিয়ে দিয়ে বলার মতো কল্পনা করে নিতে পারেন যে: "তোমার কাজ শেষ হয়ে গেলে এই নম্বরটিতে কল দিও।" এখানকার ওই কন্ট্রাক্টর বা ঠিকাদার কিন্তু কখনোই জানবে না যে সে আসলে কাকে কল দিচ্ছে — সে শুধু আপনার দেওয়া ওই নম্বরটিকে ডায়াল (dial) করে যাবে।
কলব্যাক প্যাটার্ন (Callback Pattern)
রিয়েল-ওয়ার্ল্ড বা বাস্তব জীবনের উদাহরণ: qsort (Real-World Example: qsort)
সি (C) স্টান্ডার্ড লাইব্রেরির (standard library) এই qsort ফাংশনটি মূলত যেকোনো অ্যারেকেই (array) সাজাতে বা সর্ট (sorts) করতে পারে — তা সে যেকোনো পূর্ণসংখ্যা (integers), স্ট্রিং (strings), স্ট্রাক্ট (structs), বা অন্য যাই হোক না কেন। কিন্তু কীভাবে (How)? এটি মূলত এর নিজস্ব একটি কম্প্যারেটর কলব্যাক (comparator callback) ব্যবহার করে যা এটিকে বলে দেয় যে, কীভাবে এর যেকোনো দুটি উপাদানকে (two elements) একে অপরের সাথে তুলনা (compare) করে দেখতে হয়। আপনি শুধু এর লজিকটি (logic) বা যুক্তিটি একে দিয়ে দেবেন; আর qsort নিজে থেকেই এর বাকি সর্টিং অ্যালগরিদম টুকু (sorting algorithm) করে নেবে।
এই কম্প্যারেটরটিকে (comparator) অবশ্যই যেকোনো একটি অবস্থা বা ভ্যালু রিটার্ন (return) করতে হবে:
- নেগেটিভ (Negative) যদি প্রথম উপাদানটি দ্বিতীয় উপাদানটির আগে আসে
- জিরো (Zero) বা শুন্য যদি তারা একে অপরের সমান (equal) হয়
- পজেটিভ (Positive) যদি প্রথম উপাদানটি দ্বিতীয় উপাদানটির পরে আসে
কাস্টম কম্প্যারেটর (Custom Comparator) দিয়ে qsort-এর ব্যবহার (Using qsort with a Custom Comparator)
ডিসপ্যাচ টেবিল — ফাংশন পয়েন্টারের অ্যারে (Dispatch Table — Array of Function Pointers)
যেকোনো ফাংশন পয়েন্টারের অ্যারেকে (array of function pointers) মূলত ডিসপ্যাচ টেবিল (dispatch table) বলা হয়। এখানে বড় কোনো switch স্টেটমেন্ট (statement) লেখার বদলে, আপনি মূলত একটি অ্যারের (array) ইনডেক্স ব্যবহার করতে পারেন এবং ওই পজিশনের (position) বা অবস্থানের যেকোনো ফাংশনকে সরাসরি কল বা ডাকতে (call) পারেন। ইন্টারপ্রেটার (interpreters), কমান্ড প্রসেসর (command processors) এবং স্টেট মেশিনগুলোতে (state machines) সাধারণত এই প্যাটার্নটিই বা pattern ব্যবহৃত হয়ে থাকে।