প্রিপ্রসেসর এবং ম্যাক্রো (Preprocessor & Macros)
অদৃশ্য প্রথম ধাপ (The Invisible First Step)
সি কম্পাইলার (C compiler) মূলত আপনার কোডটিকে (code) যেকোনো মেসিনের ইনসট্রাকশনে (machine instructions) অনুবাদ বা ট্রান্সলেট (translates) করার আগে, তার ভেতর দিয়ে সবার প্রথমে একটি প্রিপ্রসেসর (preprocessor) রান (runs) করে থাকে। আপনি চাইলে এটিকে একটি সাধারণ কাজ বা জব (job) করা একটি কপি-পেস্ট রোবটও (copy-paste robot) ভাবতে পারেন: যার কাজ হলো কিছু বিশেষ নির্দেশ বা ডিরেক্টিভস (directives) (যে লাইনগুলোর শুরুতে সাধারণত # থাকে) খুঁজে বের করা, সেগুলোর ওই নির্দেশ বা ইনসট্রাকশনগুলোকে (instructions) ফলো বা অনুসরণ (follow) করা, এবং আপনার সোর্স ফাইলের (source file) একটি সম্পূর্ণ নতুন ভার্সন বা সংস্করণ (new version) তৈরি করা। এর ফলে এখানকার কম্পাইলারটি (compiler) কখনোই ওই # দেওয়া লাইনগুলোকে দেখতে পায় না — সে শুধু এর রিদাল্ট বা ফলাফলটিকেই (result) দেখতে পায়।
এই প্রিপ্রসেসর (preprocessor) মূলত প্রধানত তিনটি (three main) কাজ করে থাকে:
- ফাইল ইনক্লুশন (File inclusion) — এই
#includeমূলত যেকোনো অন্য ফাইলের (another file) সম্পূর্ণ কন্টেন্টগুলোকে (contents) পেস্ট (pastes) করে দেয় - ম্যাক্রো সাবস্টিটিউশন (Macro substitution) — এই
#defineমূলত যেকোনো টেক্সটের প্যাটার্নগুলোকে (text patterns) রিপ্লেস বা পরিবর্তন (replaces) করে দেয় - কন্ডিশনাল কম্পাইলেশন (Conditional compilation) — এই
#ifdef/#ifndefমূলত যেকোনো কোডের ব্লককে (blocks of code) একসাথে ইনক্লুড বা অন্তর্ভুক্ত (includes) করে বা সেগুলোকে স্কিপ (skips) করে যায়
কনস্ট্যান্ট ডিফাইনস (Constant Defines)
প্যারামিটারাইজড ম্যাক্রো (Parameterized Macros)
ম্যাক্রোগুলো (Macros) মূলত প্যারামিটার (parameters) নিতে পারে — এগুলো দেখতে ফাংশনের (functions) মতো হলেও, তাদের কাজ করার ধরন সম্পূর্ণ আলাদা (differently)। একটি ফাংশন কল (function call) মূলত কোডের (code) অন্য আরেকটি অংশে লাফ (jumps) দিয়ে চলে যায়। কিন্তু একটি ম্যাক্রো (macro) হলো আক্ষরিক অর্থে টেক্সট রিপ্লেসমেন্ট (literally text replacement) — তাই প্রিপ্রসেসর (preprocessor) মূলত এই ম্যাক্রোটিকে (macro) যেখানেই ব্যবহার করা হয়, ঠিক সেখানেই সরাসরি এর এক্সপ্যান্ড হওয়া কোডটিকে পেস্ট (pastes) করে দেয়।
আর ঠিক এটিই মূলত এখানকার এই ম্যাক্রোগুলোকে (macros) অনেক বেশি ফাস্ট বা দ্রুত (fast) করে তোলে (যাতে কোনো ফাংশন কলের ওভারহেড বা overhead থাকে না), তবে এটি আবার একই সাথে বেশ বিপজ্জনকও (dangerous) (কারণ এখানে কোনো টাইপ চেকিং বা type checking করা যায় না, এবং এর মধ্যে অনেক অপ্রত্যাশিত পার্শ্বপ্রতিক্রিয়া বা side effects থাকার সম্ভাবনা থাকে)।
প্যারামিটারাইজড ম্যাক্রো (Parameterized Macros)
হেডার গার্ড (Header Guards)
যখন আপনি কোনো হেডার ফাইলকে (header file) #include করেন, তখন প্রিপ্রসেসর (preprocessor) মূলত ওই ফাইলের ভেতরের সমস্ত কন্টেন্টগুলোকে (contents) আক্ষরিক অর্থেই (literally) আপনার ওই সোর্সের (source) ভেতরে পেস্ট (pastes) করে দেয়। তাই যদি দুটি ফাইল আলাদাভাবে একই হেডারটিকে (header) ইনক্লুড বা অন্তর্ভুক্ত (include) করে থাকে, তবে আপনি এতে ডুপ্লিকেট ডেফিনিশনস বা দ্বৈত সংজ্ঞা (duplicate definitions) পাবেন — এবং এই নিয়ে কম্পাইলারটিও (compiler) কমপ্লেন বা অভিযোগ (complains) করতে শুরু করে দেবে।
তাই এখানকার এই হেডার গার্ডগুলো (Header guards) মূলত এটিকে আটকাতেই (prevent) কাজ করে। এগুলো মূলত #ifndef / #define / #endif ব্যবহার করে কম্পাইলারকে এটি বোঝায় যে: "শুধুমাত্র তখনই এই কন্টেন্টটিকে (content) পেস্ট (paste) করো, যখন এটি এর আগে কখনো পেস্ট করা বা pasted হয়ে যায়নি।"
হেডার গার্ডের প্যাটার্ন (Header Guard Pattern)
কন্ডিশনাল কম্পাইলেশন (Conditional Compilation)
কখনও কখনও আপনি হয়তো চাইবেন যে আপনার কোডটি (code) শুধুমাত্র কিছু নির্দিষ্ট পরিস্থিতিতেই (certain conditions) এক্সিস্ট (exists) বা কাজ করুক — ডিবাগ লগিংগুলো (debug logging) শুধুমাত্র রিলিজ বিল্ডগুলোতে (release builds) বা প্ল্যাটফর্ম-নির্দিষ্ট কোডগুলোতে (platform-specific code) (যেমন Windows বনাম Linux-এর ক্ষেত্রে) উধাও বা disappears হয়ে যায়। প্রিপ্রসেসরগুলো (preprocessor) মূলত কম্পাইলের সময় (compile time) পুরো কোডের ব্লককে (entire blocks of code) ইনক্লুড বা অন্তর্ভুক্ত (include) করতে পারে বা এক্সক্লুড বা বাদ (exclude) দিয়ে দিতে পারে।
কন্ডিশনাল ডিবাগ লগিং (Conditional Debug Logging)
অ্যাডভান্সড বা উন্নত (Advanced): স্ট্রিংজিফিকেশন বা Stringification (#) এবং টোকেন পেস্টিং বা Token Pasting (##)
ম্যাক্রোগুলোর (macros) ভেতরে মূলত এই দুটি (Two) বিশেষ অপারেটর (operators) কাজ করে থাকে:
#(স্ট্রিংজিফিকেশন বা stringification) — এটি মূলত ম্যাক্রোর যেকোনো আর্গুমেন্টকে বা parameter-কে একটি স্ট্রিং লিটারেলে (string literal) পরিণত করে##(টোকেন পেস্টিং বা token pasting) — এটি মূলত দুটি (two) ভিন্ন টোকেনকে (tokens) একসাথে আঠা বা গ্লু (glues) লাগিয়ে একটি আইডেন্টিফায়ার বা identifier তৈরি করে
এগুলো মূলত খুবই নীশ (niche) কিন্তু অনেক বেশি শক্তিশালী (powerful)। টেস্টিং ফ্রেমওয়ার্ক (testing frameworks) এবং কোড জেনারেটরগুলোতে (code generators) আপনি এগুলোকে প্রচুর দেখতে পাবেন।
স্ট্রিংজিফিকেশন এবং টোকেন পেস্টিং (Stringification and Token Pasting)
#pragma once-কে ব্যবহার করে থাকে। এটি মূলত ঠিক একই কাজ করে — অর্থাৎ এটি যেকোনো ডাবল-ইনক্লুশনকে (double-inclusion) আটকে (prevents) দেয় — তবে শুধুমাত্র একটিমাত্র লাইনেই (single line)। যদিও টেকনিক্যালি (technically) এটি কোনো স্টান্ডার্ড সি-এর (standard C) অংশ নয়, তবে প্রতিটি মেজর কম্পাইলারই (major compiler) (যেমন GCC, Clang, MSVC) মূলত এটিকে সাপোর্ট (supported) করে থাকে।