کلمات کلیدی: دستورالعملها
دستورالعملها دستوراتی هستند که به کامپایلر داده میشوند تا نحوه تفسیر کد منبع شما را کنترل کنند.
#assert
این بررسی میکند که آیا عبارت ثابت درست است و در صورت نبودن، کامپایل را متوقف میکند.
#define MOO 10
#assert MOO > 5
این خوب کامپایل خواهد شد.
#define MOO 1
#assert MOO > 5
این نخواهد شد و خطای مهلک خواهد داد. این شبیه این است:
#define MOO 1
#if MOO <= 5
#error MOO check failed
#endif
اما assert خطای زیر را خواهد داد:
Assertation failed: 1 > 5
در حالی که دومی خطای زیر را خواهد داد:
User error: Moo check failed
که ممکن است مفید باشد یا نباشد.
#define
#define
یک دستورالعمل جایگزینی متن است، هر جا که نماد اول define پیدا شود، بقیه آن جایگذاری خواهد شد.
#define MOO 7
printf("%d", MOO);
به این تبدیل خواهد شد:
printf("%d", 7);
به همین دلیل است که همه define ها در decompilation از دست میروند چون وقتی کد کامپایل میشود وجود ندارند (همه دستورالعملها pre-process میشوند). Define ها نیازی نیست حاوی اعداد باشند:
#define PL new i = 0; i < MAX_PLAYERS; i++) if (IsPlayerConnected(i)
for(PL) printf("%d connected", i);
به حلقه بازیکن که همه میشناسیم کامپایل خواهد شد (نفرت داریم). توجه کنید که چگونه پرانتزها اینجا استفاده شدهاند، برخی از for و برخی از ماکرو define (جایگزینی).
یک حقیقت کم شناخته شده دیگر در مورد define ها این است که اگر خط جدید را escape کنید میتوانند چند خطی باشند. عموماً خط جدید define را تمام میکند اما مورد زیر معتبر است:
#define PL \
new i = 0; i < MAX_PLAYERS; i++) \
if (IsPlayerConnected(i)
printf("%d", MOO(6));
این 42 را خروجی خواهد داد (نه، تصادفی انتخاب نشده). پرانتزهای اضافی در define را متوجه شدید؟ این به این دلیل است که define ها جایگزینی مستقیم متن هستند پس این به شکل زیر کامپایل خواهد شد:
printf("%d", ((6) * 7));
این همانطور که هست خوب است اما بیایید این مثال را ببینیم:
printf("%d", MOO(5 + 6));
انتظار دارید که این کامپایل شود تا 77 ((5 + 6) * 7) خروجی دهد و با پرانتز خواهد داد، اما بدون پرانتز دارید:
#define MOO(%0) \
%0 * 7
printf("%d", MOO(5 + 6));
که به این تبدیل میشود:
printf("%d", MOO(5 + 6 * 7));
که به دلیل ترتیب عملیات، به عنوان (5 + (6 * 7)) کامپایل میشود که 47 است و بسیار اشتباه. یک حقیقت جالب در مورد پارامترها این است که اگر تعداد زیادی داشته باشید، آخری همه اضافیها خواهد بود. پس انجام:
#define PP(%0,%1) \
printf(%0, %1)
PP(%s %s %s, "hi", "hello", "hi");
در واقع چاپ خواهد کرد:
hi hello hi
چون %1
حاوی "hi", "hello", "hi" است. ممکن است استفاده از #
برای تبدیل یک literal به رشته را نیز متوجه شده باشید. این ویژگی مخصوص SA-MP است و میتواند مفید باشد. فقط اینجا اضافه شده تا تمایز بین پارامترها ایجاد کند.
#else
#else
مثل else است، اما برای #if به جای if.
#elseif
#elseif
مثل else if است اما برای #if.
#define MOO 10
#if MOO == 9
printf("if");
#elseif MOO == 8
printf("else if");
#else
printf("else");
#endif
#emit
این دستورالعمل در جدول pawn-lang.pdf فهرست نشده اما وجود دارد. اساساً یک کامپایلر inline است. اگر AMX میدانید میتوانید از این برای قرار دادن opcodeهای AMX مستقیماً در کدتان استفاده کنید. تنها محدودیت این است که فقط یک آرگومان اجازه میدهد. نحو: #emit <opcode> <argument>
. <argument>
میتواند عدد گویا، صحیح یا نماد (محلی یا عمومی) (متغیرها، توابع و برچسبها) باشد. لیست opcodeها و معنی آنها را میتوان در Pawn Toolkit ver. 3664 پیدا کرد.
#endif
#endif
مثل آکولاد بسته برای if است. #if از آکولاد استفاده نمیکند، همه چیز به طور شرطی تا #endif مربوطه اضافه میشود.
#endinput, #endscript
این توقف include کردن یک فایل را متوقف میکند.
#error
این کامپایلر را فوراً متوقف میکند و پیام خطای سفارشی میدهد. برای مثال #assert را ببینید.
#if
#if
برای پیشپردازنده همان چیزی است که if برای کد است. میتوانید دقیقاً انتخاب کنید که چه چیزی کامپایل شود و چه چیزی نشود. برای مثال کد زیر را در نظر بگیرید:
#define LIMIT 10
if (LIMIT < 10)
{
printf("Limit too low");
}
این به شکل زیر کامپایل خواهد شد:
if (10 < 10)
{
printf("Limit too low");
}
که واضح است هرگز درست نخواهد بود و کامپایلر میداند - پس به شما میگوید و هشدار "constant expression" میدهد. سؤال این است که اگر هرگز درست نخواهد بود چه فایدهای دارد که اصلاً شامل شود؟ میتوانید کد را حذف کنید اما آنگاه اگر کسی LIMIT را تغییر دهد و دوباره کامپایل کند هیچ بررسیای نخواهد بود. این همان چیزی است که #if برای آن است. برخلاف if معمولی که اگر عبارت ثابت باشد هشدار میدهد، عبارات #if باید ثابت باشند. پس:
#define LIMIT 10
#if LIMIT < 10
#error Limit too low
#endif
این بررسی خواهد کرد که حد خیلی کوچک نباشد وقتی کامپایل میکنید و اگر باشد خطای زمان کامپایل خواهد داد، به جای اینکه مجبور باشید حالت را تست کنید تا ببینید چیز اشتباهی وجود دارد یا نه. این همچنین به این معناست که کد اضافی تولید نمیشود. عدم وجود پرانتز را نیز توجه کنید، میتوانید از آنها استفاده کنید، و ممکن است در عبارات پیچیدهتر به آنها نیاز داشته باشید، اما الزامی نیستند.
مثال دیگری در اینجا است:
#define LIMIT 10
if (LIMIT < 10)
{
printf("Limit less than 10");
}
else
{
printf("Limit equal to or above 10");
}
دوباره این یک بررسی ثابت است که هشدار خواهد داد، اما هر دو printf کامپایل خواهند شد وقتی که میدانیم فقط یکی هرگز اجرا خواهد شد. با استفاده از #if این به شکل زیر میشود:
#define LIMIT 10
#if LIMIT < 10
printf("Limit less than 10");
#else
printf("Limit equal to or above 10");
#endif
به این ترتیب فقط printf مورد نیاز کامپایل خواهد شد و دیگری همچنان در کد منبع شما خواهد بود در صورتی که LIMIT را تغییر دهند و دوباره کامپایل کنند، اما در کد شامل نخواهد شد چون نیاز نیست. این روش همچنین به این معناست که if بیفایده هر بار که کدتان اجرا میشود اجرا نمیشود، که همیشه خوب است.
#include
این تمام کد از فایل مشخص شده را میگیرد و در نقطهای که خط include قرار دارد در کد شما درج میکند. دو نوع include وجود دارد: نسبی و سیستم (من این اصطلاحات را ساختم، اگر بهتری دارید لطفاً بگویید). includeهای نسبی از گیومه دوتایی دور نام فایل استفاده میکنند و نسبت به فایل فعلی قرار میگیرند، پس:
#include "me.pwn"
فایل "me.pwn" را از همان دایرکتوری فایلی که آن فایل را include میکند، include خواهد کرد. نوع دیگر، سیستم، فایل را از دایرکتوری "include" که یا در همان دایرکتوری کامپایلر Pawn قرار دارد یا دایرکتوری والد (مسیرها: "include"، "../include") include میکند:
#include <me>
فایل "me.inc" (عدم وجود پسوند را توجه کنید، اگر فایل .p نباشد (نه .pwn) یا .inc میتوانید یکی مشخص کنید) را از دایرکتوری pawno/include (فرض کنیم از pawno استفاده میکنید) include خواهد کرد.
هر دو این نوعها میتوانند دایرکتوری بگیرند:
#include "folder/me.pwn"
#include <folder/me>
هر دوی اینها فایلی را یک دایرکتوری پایینتر از دایرکتوری پیشفرض مربوطه خود include خواهند کرد. اگر فایل وجود نداشته باشد کامپایل فوراً شکست میخورد.
#pragma
این یکی از پیچیدهترین دستورالعملهاست. تعدادی گزینه برای کنترل نحوه کار اسکریپت شما دارد. یک تنظیم مثال اینگونه خواهد بود:
#pragma ctrlchar '$'
این کاراکتر escape را از \ به $ تغییر میدهد، پس خط جدید، به جای "\r\n" (windows CR-LF) "$r$n" خواهد بود. بسیاری از گزینهها برای کنترل کامپایل amx برای سیستمهای embedded طراحی شدهاند و بنابراین چیزهایی را که واقعاً تقریباً نامحدود روی PC هستند محدود میکنند، همه آنها در pawn-lang.pdf فهرست شدهاند اما فقط موارد انتخابی مرتبط با SA:MP در اینجا هستند:
نام | مقدارها | توضیحات |
---|---|---|
codepage | name/value | صفحه کد Unicode را برای رشتهها تنظیم میکند. |
compress | 1/0 | در SA-MP پشتیبانی نمیشود - سعی نکنید از آن استفاده کنید. |
deprecated | symbol | اگر نماد داده شده استفاده شود هشداری تولید میکند تا به مردم بگوید نسخه بهتری موجود است. |
dynamic | value(generally a power of 2) | اندازه حافظه (در سلول) اختصاص داده شده به stack و heap را تنظیم میکند. اگر بعد از کامپایل هشدار استفاده بیش از حد حافظه دریافت کردید مورد نیاز است. (جدول عجیبی بعد از خط copyright کامپایلر) |
library | dll name | به اشتباه در SA-MP استفاده میشود. این dll را برای گرفتن توابع native تعریف شده در فایلی که از آن است مشخص میکند. فایل را به عنوان کتابخانه تعریف نمیکند. |
pack | 1/0 | معنای !"" و "" را عوض میکند. برای اطلاعات بیشتر در مورد رشتههای packed، pawn-lang.pdf را ببینید. |
tabsize | value | تنظیم دیگری که به اشتباه استفاده میشود. این باید برای تنظیم اندازه tab استفاده شود تا از هشدارهای کامپایلر که به دلیل استفاده متقابل فاصله و tab اشتباه هستند جلوگیری شود. این در SA:MP به 4 تنظیم شده چون اندازه tab در pawno همین است. تنظیم این به 0 همه هشدارهای indentation را سرکوب خواهد کرد اما بسیار نامناسب است چون کد کاملاً غیرقابل خواندن اجازه میدهد. |
unused | symbol | مثل deprecated این بعد از نمادی که میخواهید هشدار "symbol is never used" را برای آن سرکوب کنید ظاهر میشود. عموماً روش ترجیحی انجام این کار استفاده از stock است اما این همیشه قابل اعمال نیست (مثلاً پارامترهای تابع نمیتوانند کامپایل نشوند). |
Deprecated
new
gOldVariable = 5;
#pragma deprecated gOldVariable
main() {printf("%d", gOldVariable);}
این هشداری خواهد داد که gOldVariable دیگر نباید استفاده شود. این بیشتر برای توابع مفید است تا سازگاری با گذشته را حفظ کند در حالی که API را بهروزرسانی میکند.
#tryinclude
این شبیه #include است اما اگر فایل وجود نداشته باشد کامپایل شکست نمیخورد. این برای شامل کردن ویژگیها در اسکریپت شما فقط اگر شخص پلاگین صحیح نصب کرده باشد (یا حداقل include پلاگین) مفید است:
myinc.inc
#if defined _MY_INC_INC
#endinput
#endif
#define _MY_INC_INC
stock MyIncFunc() {printf("Hello");}
Gamemode:
#tryinclude <myinc>
main()
{
#if defined _MY_INC_INC
MyIncFunc();
#endif
}
این فقط MyIncFunc را فراخوانی خواهد کرد اگر فایل حاوی آن پیدا و کامپایل شده باشد. این، همانطور که قبلاً گفته شد، برای چیزهایی مثل پلاگینهای IRC خوب است تا بررسی کنند که واقعاً پلاگین دارند یا نه.
#undef
ماکرو یا نماد ثابت قبلاً تعریف شده را حذف میکند.
#define MOO 10
printf("%d", MOO);
#undef MOO
printf("%d", MOO);
این شکست خواهد خورد در کامپایل چون MOO دیگر وجود ندارد تا زمانی که دومین printf رسیده شود.
enum {
e_example = 300
};
printf("%d", e_example);
#undef e_example
printf("%d", e_example); // fatal error