نکاتی در مورد ماکرو ها در سی / سی ++

خوب باید اعتراف کنم بعد از این همه سال که دارم سی پلاس پلاس کد میزنم بازم کلی نکته راجع به این زبان باستانی وجود داره که ازشون بی خبرم و هر از گاهی با یه سری از اونها روبرو میشم. یکی از این موارد رو که امروز توی کار بهش برخوردم یه سری استفاده خاص از ماکروها هستش. تعریف ماکروطبق تعریف ارائه شده در مستندات GCC، ماکرو در حقیقت تکه کدی هستش که براش نامی رو در نظر میگیریم. هرجا از برنامه که از این نام استفاده کرده باشیم کامپایلر قبل از شروع به کامپایل اون رو با تکه کد مربوطه جایگزین میکنه.ما بطور کلی دو نوع استفاده از ماکرو داریم:نوع اول object-like macros: که برای مشخص کردن مقادیر استفاده میکنیم.نوع دوم function-like macros: ماکرو هایی که برای جایگزاری یه قطعه کد در برنامه استفاده میشن. موارد معمول استفاده از ماکروهاحالا که یه تعریف کلی از ماکرو ارائه دادیم بریم سراغ استفاده های مختلف اون در برنامه ها:۱. تعریف مقادیر ثابت پیش‌فرضیکی از استفاده هایی که از ماکرو میشه اینه که مقادیر ثابت رو در بخشی از برنامه تعریف کنیم و از اون در جاهای مختلف برنامه استفاده کنیم: #define PORT 8080 #define LOG_PATH &quot/path/foo/bar&quot #define DEBUG_MODE 1با تعریف این مقادیر ثابت در بخشی از برنامه و استفاده از نام ماکرو در بقیه برنامه دیگه لازم نیست برای تغییر یک متغیر کد برنامه رو شخم بزنیم هر استفاده ای رو اصلاح کنیم و فقط محل تعریف ماکرو رو اصلاح میکنیم.همچنین میتونیم با استفاده از pre-processor flags برنامه رو با توجه به محیط مقصد کامپایل کنیم: https://gist.github.com/ramajd/bcac47747a9a1c4abf6d3d47c27f4a3c کامپایل بصورت پیش‌فرض:$ gcc macro.c $ ./a.out compiled with: default flag تغییر MYFLAG در هنگام کامپایل:$ gcc -DMYFLAG='&quotmodified flag&quot' macro.c $ ./a.out compiled with: modified flag۲. تعریف یک عملیات خاص توسط ماکروگاهی برای انجام یک عملیات تکراری میتونیم بخشی از کد رو بصورت ماکرو بنویسیم و اون رو با ورودی‌های مختلف فراخوانی کنیم. #define MAX(a,b) (a>b ? a : b) #define EXISTS(array, vector) (vector.find(value) != vector.end())از اونجایی که کد مرتبط با ماکرو قبل از کامپایل با ماکرو جایگزین میشه سرعت برنامه در این حالت بالاتر میره، البته این مورد در مقایسه با توابع inline فرق خاصی نداره و تنها تفاوتش در اینه که توابع inline توسط کامپایلر مدیریت میشن ولی ماکرو ها توسط pre-processor مدیریت میشن.امکانات قابل استفاده در تعریف ماکرو ها ۱. تبدیل متغیر ماکرو به متندر حالت عادی متغیرها / عبارت هایی رو که به ماکرو پاس میدیم اول محاسبه (evaluate) میشن و بعد به بدنه ماکرو ارسال میشن، ولی اگه بخوایم ورودی که به ماکرو ارسال شده رو داشته باشیم میتونیم با اضافه کردن کاراکتر `#` قبل از ورودی مورد نظر اون رو بصورت یک رشته داشته باشیم: https://gist.github.com/ramajd/2630550eec8041deab7863f0273d424b خروجی برنامه:$ gcc macro2.c $ ./a.out Warning: x == 1۲. اتصال پارامترهای ماکروبعضی مواقع لازم داریم تا مقداری رو به پارامترهای یک ماکرو اضافه کنیم در این موارد میتونیم این مقاریر رو با استفاده از `##` به هم متصل کنیم. برای بهتر فهمیدن مورد استفاده این امکان ماکرو اون رو با یک مثال توضیح میدیم:‌ https://gist.github.com/ramajd/57f48261a668ab9b4599232b9af85a55 خروجی برنامه:$ gcc macro3.c $ ./a.out quit command executed. help command executed.توی این مثال با استفاده از ماکرو `MKCMD` آرایه رو با name و function مربوطه بصورت خودکار initiate کردیم.۳. ماکرو با آرگومان های متغیربرخی مواقع لازمه که ماکرو ما تعداد متغیری ماکرو داشته باشه. برای این موضوع مثل تعریف توابع با پارامترهای متغیر نام ماکرو رو با `...` مشخص میکنیم و هنگام استفاده `__VA_ARGS__` رو بکار میبریم:#define eprintf(…) fprintf (stderr, __VA_ARGS__) ... eprintf (&quot%s:%d: &quot, input_file, lineno); // above line will be replaced with following: // fprintf (stderr, &quot%s:%d: &quot, input_file, lineno)بعضی مواقع وقتی ماکرو ما خیلی پیچیده میشه میتونیم قبل از `...` یک نام رو برای بخش متغیر پارامترها تعیین کنیم و در ادامه از اون نام استفاده کنیم. فقط باید توجه کنیم که نمیتونیم این روش رو با همراه با __VA_ARGS__ استفاده کنیم.#define eprintf(args...) fprintf(stderr, args)ما همچنین میتونیم از پارامترهای با نام در کنار پارامترهای متغیر استفاده کنیم:#define eprintf(format, …) fprintf (stderr, format, __VA_ARGS__) https://gcc.gnu.org/onlinedocs/cpp/Macros.html