Czasem przydać się może możliwość elastycznego podawania parametrów funkcji. Dobrym przykładem są fakemetowe pev i set_pev, gdzie ilość parametrów określa typ zwracanych danych.
1. Umożliwienie podania dowolnej liczby parametrów
w nagłówku należy użyć ... jako ostatni argument
public funkcja(...){ } public funkcja(id, data, Float:data2, ...){ }
W tym wypadku dodatkowymi parametrami mogą być tylko liczby całkowite. Dozwolone typy możemy uwzględnić dodając Tag:
public funkcja(Float:...){ }lub kilka typów
public funkcja({Float, _, Array, CsTeams}:...){ }albo dowolny typ
public funkcja(any:...){ }
2. Określenie ilości nadesłanych argumentów
Ilość zwraca funkcja
numargs()Przykład
public plugin_init(){ test(1); test(1, 2); test(1, 2, 3); } public test(...){ new iNum = numargs(); log_amx("%d", iNum); }
3. Pobieranie danych
No i w tym miejscu wychodzą ograniczenia. Nie ma możliwości sprawdzenia jaki typ danych ma konkretny parametr ani nawet czy jest to komórka pamięci czy tablica. Musimy wiedzieć jakich się należy spodziewać. Opieramy się na pewnej umowie, np.: funkcja przyjmować może trójwymiarową tablicę Floatów lub 3 inty. Jeśli programista złamie taką umowę, działanie funkcji może zostać zatrzymane z błędem w errorlogu. Wszystkie dane pobieramy jedną funkcją:
getarg(arg, index=0)gdzie arg to numer parametru (licząc od 0) a index pozwala na pobranie tablicy.
/** * Wszystkie parametry funkcji traktuje jak inty */ test1(...){ log_amx("Test1"); log_amx("%d", numargs()); for(new i=0;i<numargs();i++){ log_amx(">> %d", getarg(i)); } }
Inne niż _ typy trzeba opatrzyć ich tagiem. Co prawda w poniższym przykładzie nie jest on konieczny, ale w wielu wypadkach musi wystąpić.
/** * Wszystkie parametry funkcji traktuje naprzemian jak inty i jak floaty */ test2({_, Float}:...){ log_amx("Test2"); log_amx("%d", numargs()); new bool:isInt = true; for(new i=0;i<numargs(); i++){ if(isInt) log_amx(">> %d", getarg(i)); else log_amx(">> %f", Float:getarg(i)); isInt = !isInt } }
To wszystko dotyczy pojedynczych komórek pamięci. Co z tablicami?
Do pobrania tablicy potrzebujemy jej rozmiaru. Tu obowiązuje ta nasza umowa i pobieramy tyle indeksów ile zapowiedzieliśmy, że chcemy
Jeśli tablica jest tekstem, wykorzystujemy budowę takiego łańcucha znaków i pobieramy kolejne komórki aż napotkamy bajt zerowy '^0'.
test3(...){ log_amx("Test3"); new iNum = numargs(); if(iNum == 1){ //Pobierz pierwszy argument jako stringa new szBuffer[32]; for(new i=0;i<sizeof szBuffer; i++){ szBuffer[i] = getarg(0, i); if(szBuffer[i] == '^0') break; } szBuffer[charsmax(szBuffer)] = '^0'; log_amx("%s", szBuffer); }else if(iNum > 1){ log_amx("%d %d", getarg(0), get_arg(1)); } }
4. Zwracanie wyniku
return oczywiście nie przestał tu działać, chodzi o referencję.
służy do tego funkcja
setarg(arg, index=0, data)
ustawia ona parametr na daną wartość, jeśli jest typu innego niż liczba całkowita usuwamy tag _:
w przypadku tablic każdy jej indeks osobno. Jeśli chodzi o stringi to ciągle interesuje nas bajt zero.
/** * data - dane * Ilość dodatkowych parametrów * 0 - zwraca data jako inta * 1 - zwraca data jak floata przez referencję * 2 - zwraca data jako stringa przez referencję (string[], len) */ test4(data, {_, Float}:...){ log_amx("Test4"); switch(numargs()){ case 1: return data; case 2: setarg(1, 0, _:float(data)); case 3:{ new szBuffer[32]; formatex(szBuffer, 31, "%d", data); new iLen = getarg(2); for(new i=0;i<iLen;i++){ setarg(1, i, szBuffer[i]); if(szBuffer[i] == '^0') break; } //Upewnij sie ze string zakonczony bajtem NULL setarg(1, iLen, '^0'); } } return 1; }