پرش به مطلب اصلی

اسکریپت‌نویسی: تگ‌ها

مقدمه

تگ پیشوندی برای متغیر است که به کامپایلر می‌گوید در شرایط خاصی با متغیر به طور ویژه رفتار کند. برای مثال می‌توانید از تگ‌ها برای تعریف جایی که متغیر می‌تواند و نمی‌تواند استفاده شود، یا روش خاصی برای جمع کردن دو متغیر استفاده کنید.

دو نوع تگ وجود دارد - تگ‌های قوی (که با حرف بزرگ شروع می‌شوند) و تگ‌های ضعیف (که با حرف کوچک شروع می‌شوند)، برای بیشتر موارد یکسان هستند اما در شرایط خاصی تگ‌های ضعیف می‌توانند بدون تگ توسط کامپایلر به صورت خاموش تبدیل شوند، یعنی هشداری دریافت نخواهید کرد، بیشتر اوقات با تگ‌های ضعیف، و همیشه با تگ‌های قوی، تغییر ضمنی تگ منجر به هشداری خواهد شد که به شما بگوید داده احتمالاً اشتباه استفاده می‌شود.

مثال بسیار ساده‌ای زیر است:

new
File:myfile = fopen("file.txt", io_read);
myFile += 4;

تابع fopen مقداری با تگ نوع File: برخواهد گرداند، در آن خط مشکلی نیست چون مقدار برگشتی در متغیری نیز با تگ File: ذخیره می‌شود (توجه کنید که case ها نیز یکسان هستند). اما در خط بعدی مقدار 4 به handle فایل اضافه می‌شود. 4 هیچ تگی ندارد (در واقع نوع تگ _: است اما متغیرها، مقدارها و توابع بدون تگ به طور خودکار به آن تنظیم می‌شوند و نیازی نیست به طور معمول نگران آن باشید) و myFile تگ File: دارد، واضح است که هیچ‌چیز و چیزی نمی‌توانند یکسان باشند پس کامپایلر هشداری صادر خواهد کرد، این خوب است چون handle فایل از نظر مقدار واقعی‌اش بی‌معنی است و بنابراین تغییر آن فقط handle را نابود خواهد کرد و به این معنی است که فایل نمی‌تواند بسته شود چون دیگر handle معتبری برای پاس دادن و بستن فایل وجود ندارد.

تگ‌های قوی

همان‌طور که در بالا ذکر شد تگ قوی هر تگی است که با حرف بزرگ شروع می‌شود. نمونه‌هایی از این‌ها در SA:MP شامل:

Float:
File:
Text:

این‌ها نمی‌توانند با انواع متغیر دیگر مخلوط شوند و همیشه وقتی سعی می‌کنید این کار را بکنید هشدار صادر خواهند کرد:

new
Float:myFloat,
File:myFile,
myBlank;

myFile = fopen("file.txt", io_read); // File: = File:, no warning

myFloat = myFile; // Float: = File:, "tag mismatch" warning

myFloat = 4; // Float: = _: (none), "tag mismatch" warning

myBlank = myFloat; // _: (none) = Float:, "tag mismatch" warning

تگ‌های ضعیف

تگ ضعیف بیشتر مثل تگ قوی رفتار می‌کند اما کامپایلر وقتی مقصد بدون تگ و منبع تگ ضعیف باشد هشدار صادر نخواهد کرد. برای مثال کدهای تگ قوی و ضعیف زیر را مقایسه کنید، اولی با تگ قوی هشدار خواهد داد، دومی با تگ ضعیف نخواهد داد:

new
Strong:myStrong,
weak:myWeak,
myNone;

myNone = myStrong; // Warning
myNone = myWeak; // No warning

اما برعکس درست نیست:

myWeak = myNone; // Warning

این در مورد توابع نیز صدق می‌کند، فراخوانی تابع با پارامتر بدون تگ، پاس دادن متغیر با تگ ضعیف هشداری نخواهد داد:

new
weak:myWeak;
MyFunction(myWeak);



MyFunction(myVar)
{
...
}

اما فراخوانی تابع با پارامتر دارای تگ (ضعیف یا قوی)، پاس دادن پارامتر بدون تگ هشدار خواهد داد. نمونه‌های تگ‌های ضعیف در SA:MP کم شناخته‌تر هستند اما اغلب استفاده می‌شوند و شامل:

bool:
filemode:
floatround_method:

استفاده

اعلان

اعلان متغیر با تگ بسیار ساده است، فقط تگ را بنویسید، نیازی به تعریف تگ قبلی به هیچ شکلی نیست اما این ممکن است و کاربردهای خود را دارد همان‌طور که بعداً آشکار خواهد شد:

new
Mytag:myVariable;

اعلان متغیر با یکی از تگ‌های موجود به شما اجازه می‌دهد آن متغیر را با توابع و عملگرهای از قبل نوشته شده برای آن نوع تگ استفاده کنید.

توابع

ایجاد تابع برای گرفتن یا برگرداندن تگ بسیار ساده است، فقط قسمت مربوطه را با نوع تگ مطلوب پیشوند کنید، برای مثال:

Float:GetValue(File:fHnd, const name[])
{
...
}

آن تابع handle فایل می‌گیرد و مقدار float برمی‌گرداند (احتمالاً مقداری از فایل خوانده شده و مطابق با نام مقدار پاس داده شده در name[]). این تابع بیشتر احتمال دارد از تابع floatstr استفاده کند، که نیز Float: برمی‌گرداند (همان‌طور که با نگاه کردن به status bar pawno وقتی روی تابع در لیست توابع سمت راست کلیک می‌کنید می‌توانید بگویید)، بعد از گرفتن رشته. پیاده‌سازی این تابع مهم نیست اما رشته را به مقدار IEEE float تبدیل خواهد کرد که سپس به عنوان سلول ذخیره می‌شود (در واقع به طور دقیق به عنوان integer ذخیره می‌شود که اتفاقاً الگوی bit یکسانی با عدد IEEE مربوطه دارد چون PAWN بدون نوع است، اما این همان چیزی است که تگ‌ها تا حدی برای مبارزه با آن وجود دارند).

عملگرها

عملگرهایی مثل +، ==، > و غیره می‌توانند برای تگ‌های مختلف overload شوند، یعنی انجام + روی دو Float: کاری متفاوت از انجام آن روی دو متغیر بدون تگ انجام می‌دهد. این مخصوصاً در مورد متغیرهای float مفید است چون همان‌طور که ذکر شد آن‌ها واقعاً float نیستند، integer هایی با الگوی bit بسیار خاص هستند، اگر عملگرها overload نمی‌شدند عملیات به سادگی روی integer ها انجام می‌شد که اگر جواب دوباره به عنوان float تفسیر می‌شد مزخرف می‌داد. به همین دلیل تگ Float: نسخه‌های overload شده بیشتر عملگرها را دارد تا توابع خاصی را برای انجام ریاضی در سرور به جای pawn فراخوانی کند.

عملگر دقیقاً مثل تابع معمولی است اما به جای نام تابع از "operator(symbol)" استفاده می‌کنید که (symbol) عملگری است که می‌خواهید بازنویسی کنید. عملگرهای معتبر عبارتند از:

+
-
=
++
--
==
*
/
!=
>
<
>=
<=
!
%

چیزهایی مثل \، *، = و غیره به طور خودکار انجام می‌شوند. چیزهایی مثل & و غیره نمی‌توانند overload شوند. همچنین می‌توانید عملگر را چندین بار با ترکیب‌های مختلف تگ overload کنید. برای مثال:

stock Float:operator=(Mytag:oper)
{
return float(_:oper);
}

اگر آن را به کدتان اضافه کنید و این کار را بکنید:

new
Float:myFloat,
Mytag:myTag;

myFloat = myTag;

دیگر هشدار کامپایلر دریافت نخواهید کرد همان‌طور که قبلاً می‌کردید چون عملگر = برای حالت Float: = Mytag: حالا مدیریت می‌شود پس کامپایلر دقیقاً می‌داند چه کار کند.

بازنویسی

در مثال overloading بالا خط عملکردی این بود:

return float(_:oper);

این مثالی از بازنویسی تگ است، _: جلوی oper یعنی کامپایلر اساساً این واقعیت که oper نوع تگ Mytag: دارد را نادیده می‌گیرد و آن را به عنوان نوع تگ _: (یعنی بدون نوع تگ) در نظر می‌گیرد. تابع float() عدد معمولی را تگ می‌کند پس باید یکی به آن پاس داده شود. در آن مثال فرض شده که Mytag integer معمولی ذخیره می‌کند اما بازنویسی باید بسیار دقیق مدیریت شود، برای مثال موارد زیر نتایج بسیار عجیبی خواهد داد:

new
Float:f1,
Float:f2 = 4.0;
f1 = float(_:f2);

منطق حکم می‌کند که f1 در نهایت 4.0 خواهد شد، اما نخواهد شد. همان‌طور که ذکر شد f2 نمایش 4.0 ذخیره می‌کند، نه فقط 4 همان‌طور که integer می‌کرد، این یعنی مقدار واقعی متغیر به عنوان integer عدد بسیار عجیبی است. بنابراین اگر به کامپایلر بگویید متغیر را به عنوان integer در نظر بگیرد به سادگی الگوی bit در متغیر را به عنوان مقدار خواهد گرفت، float را به integer تبدیل نخواهد کرد، پس عددی تقریباً تصادفی دریافت خواهید کرد (در واقع تصادفی نیست چون الگویی برای IEEE floating point وجود دارد اما چیزی شبیه 4.0 نخواهد بود).