Здравствуйте, уважаемые пользователи Pro-Pawn.ru.
Многие новички не могут разобраться в основах скриптинга потому, что не осознают самых элементарных правил не только скриптинга, но и программирования в целом.
Обычно люди сами выбирают свой стиль кодинга, но согласитесь, будет гораздо проще писать весь свой код по одному стандарту, чем работать без оного с кашей из разных стилей. Да и не стоит забывать, что код, который вы выкладываете на Pro-Pawn, кроме вас будет использоваться другими скриптерами.
Поэтому я собрал самые распространённые правила в один стиль кодинга на Pawn.
Итак, начнём:
- Глобальные переменные следует называть длинными именами.
Не используйте слишком короткие и простые имена для глобальных переменных, т.к. такие имена часто могут использоваться для локальных переменных.
Пример плохого кода:
Как видно, в данном примере внутри функции PrintPos создаются локальные переменные x, y и z, но эти имена уже заняты. Происходит коллизия имён - конфликт между переменными/функциями/константами с одинаковыми названиями.PHP код:
new Float:x, Float:y, Float:z; // координаты спавна (глобальные переменные)
stock SetSpawnPos()
{
x = 0.0, y = 1.0, z = 2.0; // здесь эти глоб. переменные используются
}
stock PrintPos(playerid)
{
new Float:x, Float:y, Float:z; // а здесь компилятор выдаст предупреждение,
// т.к. переменные "x", "y" и "z" уже были объявлены
// в качестве глобальных переменных
GetPlayerPos(playerid, x, y, z);
printf("%.4f, %.4f, %.4f", x, y, z);
}
Вывод: если создаёте глобальные переменные, задавайте им достаточно длинные и осмысленные названия, чтобы не было конфликтов имён с локальными переменными.
Уточним названия переменных: добавим слово "spawn", чтобы было понятно, что это координаты спавна.
Пример хорошего кода:
Теперь локальные переменные больше не будут конфликтовать с глобальными, т.к. мы добавили к названиям глобальных переменных префикс "spawn_". Этот префикс выбран потому, что в переменных сохраняются координаты спавна (ваш Кэп). Таким образом удалось избежать коллизии имён.PHP код:
new Float:spawn_x, Float:spawn_y, Float:spawn_z; // координаты спавна (глобальные переменные)
stock SetSpawnPos()
{
spawn_x = 0.0, spawn_y = 1.0, spawn_z = 2.0; // здесь эти глоб. переменные используются
}
stock PrintPos(playerid)
{
new Float:x, Float:y, Float:z; // ok (имена "x", "y" и "z" не заняты глобальными переменными)
GetPlayerPos(playerid, x, y, z);
printf("%.4f, %.4f, %.4f", x, y, z);
}
- Не стоит писать названия переменных, констант и функций на транслите. Это должно быть очевидно, как убого выглядит код, написанный на транслите или на ломанном английском.
Если же у вас проблемы с иностранными языками - учите английский. Либо завязывайте со скриптингом, потому что знание английского - неотъемлимая часть в обучении программированию.
- Придерживайтесь одного и того же стиля задания имён для переменных, констант и функций.
Ниже описан самый распространённый стиль среди сообщества SA:MP:Пример:
- Имена констант пишутся большими буквами, слова в названии разделяются знаком подчёркивания (_).
- Названия переменных рекомендуется писать маленькими буквами, слова так же разделяются символом подчёркивания.
- В именах функций каждое слово пишется с большой буквы, при этом слова не разделяются никакими знаками.
- Часть имени переменной может писаться большими буквами, если это сокращение (например, "HTTP_response_code", "forum_URL", "TD_health"). То же самое исключение относится и к именам функций ("HTTPCallback", "OpenURL").
Делается это для того, чтобы потом по имени можно было легко понять, константа ли это, переменная или функция.PHP код:
// переменная
new logged_players_count;
// константа
const MAX_HOUSES = 256; // либо #define MAX_HOUSES 256
// функция
stock GetMaxHouses()
{
return MAX_HOUSES;
}
Примечание: в SA:MP также есть функции "db_open", "tickcount", "printf", и т.д., но все они - всего лишь отголоски тех времён, когда никто особо не задумывался о стандартах. Все писали код по-своему, отсюда и путаница.
Потому логично считать те функции не правилом, а исключением из правил.
- Названия глобальных переменных, констант и функций должны говорить об их назначении.
Плохой пример:
Из названия этих переменных понятно только то, что это текстдравы, а что это за текстдравы и для чего они предназначены - гадайте сами! Больше никакого смысла в этих названиях нет.PHP код:
Textdraw0, Textdraw1, Textdraw2, ...
Придётся лишний раз выискивать все места, где используются эти переменные, чтобы понять, для чего они нужны.
Хороший пример:
Здесь сразу становится ясно, что это текстдрав логотипа сервера. При этом префикс "TD" указывает на то, что в переменной хранится ID текстдрава.PHP код:
TD_server_logo;
Использование префиксов и постфиксов в переменных и константах не обязательно, но всё же рекомендуется, т.к. с ними значительно легче определить тип переменной и область видимости: не нужно смотреть место, где объявлена переменная/константа, достаточно лишь посмотреть на её название.
Список часто используемых префиксов:
- "DLG" для констант с номерами диалогов.
- "obj" для переменных под ID объектов.
- "pobj" для объектов, создающихся для отдельных игроков (функцией CreatePlayerObject).
- "TD" для текстдравов (большими буквами потому, что в сокращение попадают только большие буквы из "TextDraw").
- "PTD" для текстдравов, создающихся для отдельных игроков (PlayerText).
- Старайтесь правильно табулировать код (или, как это ещё называют, писать код лесенкой).
Избегайте каши из пробелов вперемешку с табами - её трудно заметить в редакторе кода, но на форуме из-за такой каши часто становятся заметны проблемы с табуляцией.
Желательно табулировать код только с помощью Tab, т.к. во многих редакторах кода можно настроить под свой вкус их ширину (т.е. 1 таб можно сделать равным 3 пробелам вместо 4), чего нельзя сделать с пробелами. Кроме того, при неправильной табуляции лишний таб заметить намного легче, чем пробел.
Отыскивать лишние пробелы удобнее всего в Notepad++ с включенным отображением невидимых символов.
Помните: правильно оттабулированный код - залог понимания другими скриптерами.
- Не пытайтесь записывать весь код в одну строку - компилятору нет дела до стиля оформления исходного кода, результат в AMX будет один и тот же.
Зато таким "утрамбовыванием" вы можете здорово испортить читаемость кода для себя и других скриптеров.
Пример плохого кода (взято прямикомиз адаиз RLS):
Исправим ситуацию:PHP код:
if (strcmp(cmd,"/stuff",true)==0){new string[32];
format(string,sizeof(string),"Ваш уровень: %d",GetPlayerScore(playerid));SendClientMessage(playerid,-1,string);return 1;}
Примечание: на самом деле утрамбовывание строк может привести к уменьшению файла .amx, но это только из-за отладочной информации. Чем больше строк в исходном коде, тем больше весит отладочная информация (~2 байта на строку, результат может варьироваться из-за сжатия содержимого AMX), но на характеристиках выполнения скрипта это никак не сказывается. Если вы хотите, чтобы ваш скрипт меньше весил и сервер тратил меньше памяти при выполнении скрипта, есть смысл вообще убрать всю отладочную информацию - для этого в папке с компилятором (pawncc) создайте файл pawn.cfg и пропишите в нём "-d0".PHP код:
if (strcmp(cmd, "/stuff", true) == 0)
{
new string[32];
format(string, sizeof(string), "Ваш уровень: %d", GetPlayerScore(playerid));
SendClientMessage(playerid, -1, string);
return 1;
}
- Избегайте использования "магических чисел", которые никто, кроме вас, объяснить не может.
Плохой пример:
Почему пример плохой? Хотя бы потому, что не сразу понятно, откуда взялось число 10 в условии выхода из цикла.PHP код:
new some_array[10];
public OnGameModeInit()
{
for (new i = 0; i < 10; i++)
some_array[i] = random(2);
}
CMD:printarr(playerid, params[])
{
for (new i = 0; i < 10; i++)
printf("%d", some_array[i]);
}
Кроме того, попробуйте поменять размер массива - придётся перерывать весь мод, выискивая каждый цикл, работающий с тем массивом, чтобы заменить размер на новый. Многие начинающие скриптеры именно так и забывают сменить размер в циклах, после чего в работе сервера часто возникают ошибки из-за выхода за пределы массива (привет RLS-никам!)
Хороший пример:
Теперь достаточно лишь один раз сменить размер массива в месте его объявления, остальную работу компилятор возьмёт на себя. Такой код будет куда более надёжным.PHP код:
new some_array[10];
public OnGameModeInit()
{
for (new i = 0; i < sizeof(some_array); i++)
some_array[i] = random(2);
}
CMD:printarr(playerid, params[])
{
for (new i = 0; i < sizeof(some_array); i++)
printf("%d", some_array[i]);
}
Ещё пример:
Здесь размер массива определяется константой, что тоже приемлемо в некоторых ситуациях. Например, во многих системах домов есть константа "MAX_HOUSES" и было бы удобнее использовать её вместо "sizeof(house_info)".PHP код:
const SOME_ARRAY_SIZE = 10;
new some_array[SOME_ARRAY_SIZE];
public OnGameModeInit()
{
for (new i = 0; i < SOME_ARRAY_SIZE; i++)
some_array[i] = random(2);
}
CMD:printarr(playerid, params[])
{
for (new i = 0; i < SOME_ARRAY_SIZE; i++)
printf("%d", some_array[i]);
}
- Используйте константы при каждой возможности.
Плохой пример:
А теперь вопрос: почему же пример плохой?PHP код:
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if (newkeys == 327808)
{
// ...
}
}
- В новых версиях сервера SA:MP значения констант могут измениться. Константы SA:MP сделали разработчики мультиплеера, они же имеют право и изменить их в следующих версиях, если это будет нужно.
В этом случае, если вы используете числа вместо констант, вам придётся перешаривать весь мод, вручную выискивать все нужные числа (среди over 9000 других чисел) и заменять их.
Да, в стагнирующем проекте типа SA-MP такие изменения маловероятны, но это ещё не отменяет их возможности. Использование "магических чисел" вместо констант считается плохой практикой.- Для того, чтобы понять, какие клавиши проверяются в примере, придётся смотреть таблицу (либо постоянно держать эти числа в голове), вместо того, чтобы просто прочитать их названия на английском языке и сразу всё понять. Если же у вас проблемы с иностранным языком - см. пункт 2.
Хороший пример:
В этом примере названия клавиш уже расписаны на английском языке и не составит никакого труда понять их.PHP код:
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if (newkeys == KEY_AIM | KEY_YES | KEY_CTRL_BACK)
{
// ...
}
}
Ещё пример:
Чтобы правильно составлять диалоги и обработку пользовательского ввода в них, придётся либо заучивать ID всех диалогов наизусть (а их в моде может накопиться под несколько сотен), либо каждый раз перед использованием ShowPlayerDialog заглядывать в OnDialogResponse и выискивать там нужный ID. В любом случае это лишняя работа для скриптера.PHP код:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch (dialogid)
{
case 0:
return 1;
case 1:
{
if ((inputtext[0] != '\0') && (0 == strcmp(inputtext, player_info[playerid][pPassword])))
player_login_status{playerid} = true;
return 1;
}
case 2:
{
// ...
}
// ...
}
return 0;
}
А теперь представим такую ситуацию: в моде 100 разных диалогов и вдруг понадобилось удалить диалог под номером 50 - вы не сможете просто так взять и поставить диалог #51 на место #50, #52 на место #51 и т.д. Вам придётся либо потратить уйму времени на перестановки, либо оставить 50-й ID диалога неиспользованным.
Решение: использовать именованные константы для ID всех диалогов.
PHP код:
enum
{
// Компилятор сам распределит значения: константа DIALOG_ID_NONE будет равна нулю, DIALOG_ID_LOGIN - 1, DIALOG_ID_REGISTER - 2 и т.д.
// Если захотите удалить DIALOG_ID_LOGIN, то DIALOG_ID_REGISTER вместо 2 станет равна 1
// и значения всех последующих констант тоже уменьшатся на единицу.
DIALOG_ID_NONE,
DIALOG_ID_LOGIN,
DIALOG_ID_REGISTER,
// ...
};
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch (dialogid)
{
case DIALOG_ID_NONE:
return 1;
case DIALOG_ID_LOGIN:
{
if ((inputtext[0] != '\0') && (0 == strcmp(inputtext, player_info[playerid][pPassword])))
player_login_status{playerid} = true;
return 1;
}
case DIALOG_ID_REGISTER:
{
// ...
}
// ...
}
return 0;
}
- Константы, обозначающие неправильный порядковый номер (ID) или количество чего-либо, должны быть равны -1.
Рассмотрим это правило на примере константы, означающей, что у игрока нет дома.
Плохой пример:
Но что будет, если вы потом захотите создать больше 255 домов? Сделаете 255-й дом на координатах, куда не сможет пробраться обычный игрок (например, под землёй)? Это уже костыль. Решается же эта проблема очень просто.PHP код:
const MAX_HOUSES = 100; // На сервере всего 100 домов.
const INVALID_HOUSE_ID = 255; // Если в player_info[playerid][pHouseKey] число 255, то у игрока нет дома.
// Почему бы и нет, ведь у меня на сервере только дома с ID от 0 до 254?
Хороший пример:
Похожая проблема была в моде GodFather (интересно, кто-нибудь из "нынешнего поколения" видел этот мод?): там число 255 использовали как неправильный ID игрока. Потом вышел SA-MP 0.3a, в котором к серверу могли подключиться до 500 игроков - тогда 255 перестал быть неправильным ID, что приводило к багам и весьма неожиданным последствиям для тех, кому не повезло зайти под 255-м ID.PHP код:
const MAX_HOUSES = 300;
const INVALID_HOUSE_ID = -1; // Число -1 не будет конфликтовать с макс. кол-вом домов, т.к. число домов не может быть отрицательным.
- Не путайте целые числа с вещественными.
Пример плохого кода:
Казалось бы, ничего особенного. Но давайте посмотрим, во что компилятор превращает этот код:PHP код:
new Float:some_float = 10;
new some_float_sqr = floatpower(some_float, 2);
new Float:health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + 10);
Видите эти лишние вызовы функции float()? Компилятор подставляет их, если вы используете целые числа там, где нужно использовать вещественные.PHP код:
new Float:some_float = float(10);
new some_float_sqr = floatpower(some_float, float(2));
new Float:health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + float(10));
Это не смертельно, но может увеличить нагрузку на сервер, ведь он будет тратить лишнее время на то, чтобы преобразовать число из целочисленного формата в число с плавающей запятой (Float) - просто потому, что кто-то поленился дописать ".0" в конце числа.
Пример хорошего кода:
Как видите, достаточно просто добавить ".0" к числам, чтобы сделать их вещественными. Это не трудно.PHP код:
new Float:some_float = 10.0;
some_float += 2.0;
new some_float_sqr = floatpower(some_float, 2.0);
new Float:health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + 10.0);
Главное только, наоборот, не указывать вещественные числа там, где нужны целые, иначе это может привести к ошибкам.
- Старайтесь, чтобы ваш код укладывался в лимит: 80 символов на строку. Если не вмещается - переносите.
В коде будет больше строк, но зато вам не придётся лишний раз тянуться к полосе горизонтальной прокрутки в редакторе кода, чтобы посмотреть на то, что не влезает на экран.
Обычно лимит в 80 символов выделяется линией в pawno, SynWrite и многих других редакторах кода, аналогично полям в школьных тетрадях.
Пример плохого кода:
Строка кода занимает 155 символов, придётся тянуться за полосой горизонтальной прокрутки в редакторе, чтобы увидеть последние параметры.PHP код:
SetObjectMaterialText(objectid, "Sample text", 0, OBJECT_MATERIAL_SIZE_512x512, "Arial", 12, 1, 0x00000000, 0xFFFFFFFF, OBJECT_MATERIAL_TEXT_ALIGN_CENTER); //155
Исправим ситуацию, перенеся параметры функции на новые строки:
Цель достигнута: самая длинная строка кода занимает 77 символов, что укладывается в лимит <=80.PHP код:
SetObjectMaterialText(
objectid, "Sample text", 0, OBJECT_MATERIAL_SIZE_512x512,
"Arial", 12, 1, 0x00000000, 0xFFFFFFFF, OBJECT_MATERIAL_TEXT_ALIGN_CENTER //77 симв.
);
Ещё пример:
Исправляем, сгруппировав координаты и углы поворота:PHP код:
CreateObject(404, 1138.000, 146.0000, 0.0000, 0.0000, 0.0000, 128.0000, 500.0000); //82
Или даже так, сэкономив пространство по вертикали:PHP код:
CreateObject(
404,
1138.000, 146.0000, 0.0000, //31 символ
0.0000, 0.0000, 128.0000,
500.0000
);
И ещё один пример:PHP код:
CreateObject(
404, 1138.000, 146.0000, 0.0000, 0.0000, 0.0000, 128.0000, 500.0000); // 73
Переносим параметры:PHP код:
format(buffer, sizeof(buffer), "Имя: %s\nУровень: %d\nДеньги: %d$\nАдрес: %s, %d\nОрганизация: %s\n", player_name, level, money, player_city, player_house_id, player_family_name); //179
Обратите внимание: строковое значение разбито на несколько строк. Также их уровень табуляции увеличен на 1, чтобы показать, что это одна строка, а не 5 разных строк.PHP код:
format(
buffer, sizeof(buffer),
"Имя: %s\n"\
"Уровень: %d\n"\
"Деньги: %d$\n"\
"Адрес: %s, %d\n"\
"Организация: %s",
player_name, level, money, player_city, // 43
player_house_id, player_family_name
);
- Тело циклов for, do и while (если они не пустые) и ветвления if следует переносить на отдельную строку для лучшей читаемости.
Плохой пример:
Хороший пример:PHP код:
if (0 == IsPlayerAdmin(playerid)) return SendClientMessage(playerid, -1, "Ошибка: Вы не администратор!");
PHP код:
if (0 == IsPlayerAdmin(playerid))
return SendClientMessage(playerid, -1, "Ошибка: Вы не администратор!");
- Аббревиатуры в названиях должны записываться в верхнем регистре.
Примеры:
PHP код:
// HTTP - HyperText Transfer Protocol
native HTTP(index, type, url[], data[], callback[]);
// NPC - Non-Player Character
native ConnectNPC(name[], script[]);
forward OnNPCSpawn();
// EOF - End Of File
const EOF = -1;
- Для функций, выполняющих проверку чего-либо, следует использовать префикс Is.
Примеры:
Код:// из стандартных функций SA:MP native IsObjectMoving(playerid); native IsPlayerConnected(playerid); native IsPlayerInAnyVehicle(playerid); // примеры пользовательских функций stock bool:IsHouseUnoccupied(houseid); stock IsRPNick(name[]); // Название кликабельно.- Префиксы Get/Set должны использоваться в названиях функций, которые получают или изменяют (устанавливают) значение какого-либо атрибута.
Таким атрибутом может быть здоровье игрока, его уровень, игровое время на сервере - что угодно, к чему нельзя получить доступ из скрипта напрямую, как к переменной, но можно получить/изменить с помощью функции.
Примеры:
Код:// из стандартных функций SA:MP native GetPlayerPos(playerid, &Float:x, &Float:y, &Float:z); native SetPlayerPos(playerid, Float:x, Float:y, Float:z); native GetVehicleHealth(vehicleid, &Float:health); native SetVehicleHealth(vehicleid, Float:health); native SetGravity(Float:gravity); native SetWeather(weatherid); // примеры пользовательских функций stock GetHouseValue(houseid); stock SetHouseValue(houseid, price); stock GetBusinessFee(businessid); stock SetBusinessFee(businessid, fee);- Массивы, у которых в ячейках сохраняются только значения от 0 до 255, следует объявлять с указанием слова char в размере, чтобы сэкономить кол-во отводящейся под массив памяти, используя под ячейку всего 1 байт вместо 4.
Доступ к ячейкам таких массивов должен осуществляться с помощью фигурных скобок ({}) вместо квадратных ([]).
Пример:
PHP код:
new player_login_status[MAX_PLAYERS char];
#define IsPlayerLoggedIn(%0) (player_login_status{%0})
public OnPlayerConnect(playerid)
{
player_login_status{playerid} = 0;
// ...
}
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
if (dialogid == DIALOG_ID_LOGIN)
{
player_login_status{playerid} = 1;
// ...
}
// ...
}
CMD:do(playerid, params[])
{
if (0 == IsPlayerLoggedIn(playerid))
return SendClientMessage(playerid, -1, "Ошибка: Вы не залогинены!");
// ...
}
- Инклуды (.inc) должны содержать защиту от повторного подключения (include guard).
Раньше этот метод защиты был совершенно не нужен, поскольку сам компилятор Pawn не позволял подключить один и тот же файл дважды.
Однако эта возможность работала не на всех платформах (некорректная работа в Linux и OS X), из-за чего скрипты, компилировавшиеся под Windows могли не скомпилироваться под OS X.
В новой версии компилятора от Zeex данная возможность убрана, чтобы компилятор одинаково работал на всех платформах.
Поэтому теперь есть смысл самостоятельно защищать свои файлы от повторного использования.
Пример:
PHP код:
// файл: my_functions.inc
#if defined MY_FUNCTIONS_INC // если файл уже был подключен ранее -
#endinput // прервать дальнейшую обработку файла
#endif
#define MY_FUNCTIONS_INC // объявить макрос, чтобы компилятор в следующий раз
// "знал" о том, что этот файл уже был подключен
// ваш код
#include <a_samp>
//...
- Переменные, находящиеся внутри инклуда, должны быть доступны только внутри него с помощью атрибута static.
Если же нужно получить/изменить значение такой переменной извне, можно сделать для этого отдельные функции:
Пример:
PHP код:
// файл: jetpack.inc
static stock player_has_a_jetpack[MAX_PLAYERS char];
stock IsPlayerInAJetpack(playerid)
return player_has_a_jetpack{playerid};
- В инклудах должна быть возможность настройки и задания параметров из скрипта, в котором инклуд подключается.
Рассмотрим это на примере простого античита, который через заданный интервал времени проверяет, есть ли у игроков запрещённое оружие (пулемёт или гранатомёт, например), и при его наличии банит.
Античит через заданный интервал времени производит проверку всех игроков на наличие запрещённого оружия. Время проверки настраивается с помощью макроса MY_ANTICHEAT_CHECK_INTERVAL (2000 мс по умолчанию).
Плохой пример:
Редактирование настроек внутри инклуда - очень грязный вариант. Во-первых, сам по себе инклуд является зоной ответственности автора инклуда, а не пользователя. Во-вторых, при выходе обновления инклуда пользователю придётся заново редактировать файл, чтобы восстановить прежние настройки - и так с каждой новой версией (особенно "приятно" вспоминать, какие настройки были выставлены в предыдущей версии, если бросил новую версию инклуда в папку "includes" с заменой старой версии, а из старой забыл заранее скопировать настройки).PHP код:
// файл: my_anticheat.inc
// Нужны более частые проверки - отредактируйте инклуд.
// Если выйдет новая версия инклуда - редактируйте заново.
#define MY_ANTICHEAT_CHECK_INTERVAL 2000
// ...
Хороший пример:
PHP код:
// файл: mode.pwn
#define MY_ANTICHEAT_CHECK_INTERVAL 500 // Перед тем, как подключить инклуд с античитом, задаём интервал проверки.
#include "my_anticheat.inc" // Подключаем инклуд (проверка будет выполняться примерно через каждые 500 мс).
Как видите, в инклуд потребовалось добавить всего пару строк. Это не так уж и сложно.PHP код:
// файл: my_anticheat.inc
#if !defined MY_ANTICHEAT_CHECK_INTERVAL // Если интервал не был задан в скрипте перед подключением инклуда...
#define MY_ANTICHEAT_CHECK_INTERVAL 2000 // ... устанавливаем время по умолчанию
#endif
Теперь чтобы изменить те или иные настройки, пользователю не придётся лезть в сам инклуд.
По такому же принципу оформлены настройки в YSI (например, _YSI_NO_VERSION_CHECK отключает проверку обновлений YSI при каждой загрузке скрипта) и других профессионально реализованных инклудах. Кроме того, аналогичная практика используется во многих фреймворках и библиотеках на C/C++.
В дальнейшем содержимое темы будет исправляться и дополняться.
P.S.: Стрельба себе в ногу - дело добровольное. Я никому этого не запрещаю.
Автор: Daniel_Cortez
Специально для Pro-Pawn.ruКопирование данной статьи на других ресурсах без разрешения автора запрещено!