PDA

Просмотр полной версии : [Вопрос] Вынесение проверок в функции



Elrmrnt-Kritik
01.06.2018, 22:15
Сейчас пересматривал свои последние системы и столкнулся с такой проблемой, которую я же сам и создал. Зачастую у меня одна и та же проверка есть и в функции, и перед тем, как вызывается эта функция. Где ее лучше оставить, а где все-таки лучше снести?
Пример кода:

#define DLG DialogResponse
enum
{
DLG_ENTER_PASSWORD,
}
static const
MIN_LENGTH_PASSWORD = 6,
MAX_LENGTH_PASSWORD = 16;

// неважно где
ShowPlayerDialog(playerid, DLG_ENTER_PASSWORD, DIALOG_STYLE_PASSWORD, "Текст", "Введите пароль", "Ок", "Закрыть");
DLG:DLG_ENTER_PASSWORD(playerid, response, listitem, inputtext[])
{
if(!response)
return 1;

new length = strlen(inputtext);
if(!(MIN_LENGTH_PASSWORD <= length <= MAX_LENGTH_PASSWORD))
return 1;
else if(!IsValidPassword(inputtext))
return 1;
// неважно что
return 1;
}
stock IsValidPassword(password[])
{
new length = strlen(password);
if(!(MIN_LENGTH_PASSWORD <= length <= MAX_LENGTH_PASSWORD))
return 0;

new string[33+(-2+2)*2+1] = "|[A-Za-z0-9#$-_+=;:@!?]{%d,%d}|";
format(string, sizeof string, string, MIN_LENGTH_PASSWORD, MAX_LENGTH_PASSWORD);
return regex_match(password, string);
}


Попутно так еще одну вещь хотел бы уточнить все по этому же коду. Понятно, что в моей функции IsValidPassword можно было бы обойтись без формата, если бы MIN_LENGTH_PASSWORD и MAX_LENGTH_PASSWORD были макросами. Но я решил это сделать константами. Вообще сейчас все лимиты и значения стараюсь указывать через константы. Что думаете по этому поводу? Лучше переводить на макросы и забыть про format в таких ситуациях?

- - - Добавлено - - -

Еще вспомнил, что хотел узнать уже давно, но все забывал. Если нужно, пожалуйста, перенесите в отдельную тему. Как вы смотрите на моды, написанные исключительно на C++? Ведь в них идет постоянный вызов нативных функций, что по идее плохо влияет на скорость работы. Но в то же время работа всех функций в C++ идет быстрее. Компенсируется ли быстрота работы кода в C++ вызовом нативных функций из мода?

DeimoS
01.06.2018, 23:05
Где ее лучше оставить, а где все-таки лучше снести?

Оставляй проверку везде, где результат работы функции зависит от значения переменной, которое проверяется в условии.
Если тебя пугает то, что на выполнение условия тратится время, то эти траты ничтожно малы в сравнении с возможными ошибками и багами из-за невалидных данных. А если вдруг какая-то из подобных функций вызывается, например, в цикле или таймере, где действительно важно экономить каждую миллисекунду, лучше просто продублируй нужный код из функции внутри этого таймера/цикла, убрав ненужные проверки. Но убирать проверки внутри самих функций я бы не советовал. Может аукнуться рано или поздно.



Попутно так еще одну вещь хотел бы уточнить все по этому же коду. Понятно, что в моей функции IsValidPassword можно было бы обойтись без формата, если бы MIN_LENGTH_PASSWORD и MAX_LENGTH_PASSWORD были макросами. Но я решил это сделать константами. Вообще сейчас все лимиты и значения стараюсь указывать через константы. Что думаете по этому поводу? Лучше переводить на макросы и забыть про format в таких ситуациях?

Как вариант, просто делай макрос + константу, а-ля

#define MSG_MIN_LENGTH_PASSWORD 64
const MIN_LENGTH_PASSWORD = MSG_MIN_LENGTH_PASSWORD;
И уже там, где нужно форматировать текст, используй макрос, а в условиях используй константу.
Правда не уверен, что в этом будет большой смысл, если честно.

Можно ещё придумать какой-нибудь такой костыль с единоразовым форматированием:

static test_message[27+2+1];
if(isnull(test_message))
{
format(test_message, sizeof(test_message), "Минимальный размер пароля: %d", MIN_LENGTH_PASSWORD);
}
SendClientMessage(playerid, -1, test_message);
Только стоит понимать, что такой вариант скушает в два раза больше памяти, так как помимо того, что нужно выделить место под массив, так же и строка в format должна где-то хранится до того, как попадёт в наш массив. В итоге получится, что она продублируется в памяти (один раз без значения макроса, а второй раз, находясь в массиве, со значением).
Это, конечно, не критично, ибо таких ситуаций в SA-MP скриптинге полно. Просто рассказываю, что и у этого варианта есть свои "минусы".


Еще вспомнил, что хотел узнать уже давно, но все забывал. Если нужно, пожалуйста, перенесите в отдельную тему. Как вы смотрите на моды, написанные исключительно на C++? Ведь в них идет постоянный вызов нативных функций, что по идее плохо влияет на скорость работы. Но в то же время работа всех функций в C++ идет быстрее. Компенсируется ли быстрота работы кода в C++ вызовом нативных функций из мода?

Само по себе использование С++, Assembler, Lua, Pawn или какого-либо другого ЯП не даёт ничего. Говнокод можно писать на любом языке. Так же, как и для того, чтоб писать хороший код, нужно иметь достаточно знаний.
Ну а вообще, лично я считаю это бессмысленным извращением в любом случае. SA-MP прекрасно работает и с модификациями, написанными на Pawn, поэтому даже если представить, что мод на каком-то другом языке будет работать быстрее, от этого толку будет не очень много.

Elrmrnt-Kritik
01.06.2018, 23:56
Только вот с памятью не сообразил. Понятно, что сам текст попадет в сегмент данных, с этим вроде как уже давно все уяснил. Также понятно, что выделится место в памяти (static test_message[27+2+1];). Ведь это и есть двойной расход памяти? А как же такая ситуация:



new string[27+2+1];
format (http://wiki.sa-mp.com/wiki/format)(string, sizeof string, "Минимальный размер поля: %d", MIN_LENGTH_PASSWORD);




По сути тоже самое: содержимое текста попадает в сегмент данных, только вот "ликвидируется" переменная string. (я смотрел не так давно лекцию институтскую насчет памяти, где рассказывалось про то, как система понимает начало и конец определенной переменной, как ее добавляет и удаляет, про структурки MCB). Так вот, ко второму варианту нет же придирок. А в чем тогда беда собственно? Что костыль постом выше лишь не "удаляет" эту переменную из памяти?
(а как вообще правильно назвать "удаление" переменной из памяти?)

DeimoS
02.06.2018, 02:09
Только вот с памятью не сообразил. Понятно, что сам текст попадет в сегмент данных, с этим вроде как уже давно все уяснил. Также понятно, что выделится место в памяти (static test_message[27+2+1];). Ведь это и есть двойной расход памяти?

Да, это и будет двойной расход (и массив, и текст "раздуют" сегмент данных, который не изменяется во время работы сервера стандартными средставми).


А как же такая ситуация:



new string[27+2+1];
format (http://wiki.sa-mp.com/wiki/format)(string, sizeof string, "Минимальный размер поля: %d", MIN_LENGTH_PASSWORD);




По сути тоже самое: содержимое текста попадает в сегмент данных, только вот "ликвидируется" переменная string.

Нет, не то же самое. Тут массив будет создан в стэке и после обработки блока кода, в котором создан массив, память, зарезервированная этим массивом, станет доступна для перезаписи. Соответственно, двойного потребления памяти уже не будет, но...


А в чем тогда беда собственно? Что костыль постом выше лишь не "удаляет" эту переменную из памяти?
(а как вообще правильно назвать "удаление" переменной из памяти?)

...беда как раз в постоянном "насиловании" стэка. То бишь, каждый раз, когда будет вызываться этот код, часть стэка сначала будет резервироваться для массива, а потом в этот массив будет заносится текст из сегмента данных. Это так же не смертельно, но и бессмысленно с учётом того, что строка не будет изменятся во время работы сервера.

Самым идеальным вариантом будет либо полный отказ от констант в пользу макросов, либо макрос + константа. На деле оба варианта идентичны, ибо и макрос, и константа после компиляции превратятся в то число, которое присвоено этим макросу/константе (макрос "развернётся" во время работы препроцессора, а константы "развернёт" компилятор). Разница тут лишь в читаемости кода (ну дополнительную информацию нужно будет помнить. Хотя если проработать нормальную систему префиксов для таких макросов, то проблем быть не должно)

Elrmrnt-Kritik
15.06.2018, 01:18
Хотел уже попросить закрыть тему, только еще одно предложение заметил: "константа после компиляции превратятся в то число, которое присвоено этим макросу/константе". Константа же попадет в сегмент данных и будет там сидеть, ее адрес везде будет использоваться. А макрос засунет в десятки мест одно и то же число и будет в сегменте десятки мест под это число. Или для чисел такое неактуально?

Daniel_Cortez
15.06.2018, 01:50
Константа же попадет в сегмент данных и будет там сидеть, ее адрес везде будет использоваться.
В секцию данных попадают глобальные переменные, а у констант значения подставляются прямиком в код (т.е. в каждую инструкцию AMX, в которой они используются).

DeimoS
15.06.2018, 17:12
Константа же попадет в сегмент данных и будет там сидеть, ее адрес везде будет использоваться. А макрос засунет в десятки мест одно и то же число и будет в сегменте десятки мест под это число. Или для чисел такое неактуально?

Дополню ответ выше, дабы было понятнее:
const name = 12;
new const name = 12;
static const name = 12;
static const name[MAX_PLAYER_NAME] = "DeimoS";
Константой тут является лишь код на первой строке. Всё остальное - переменная с флагом "read-only".

Elrmrnt-Kritik
15.06.2018, 23:51
А, ну, то есть только числа подставляются в итоговый вариант, если присутствует модификатор const. А в остальных (трех последних) случаях значения переменных с флагом "read-only" будут заменяться адресами к ячейкам? На значение этой переменной соответственно.

DeimoS
16.06.2018, 10:58
const name = 12;
Будет обрабатываться практически так же, как обычный макрос. Я тебе в другой твоей теме расписывал всё на этот счёт.
Остальные случаи обрабатываются так же, как обрабатываются переменные (ибо это и есть переменные)