Kai kuriose programavimo kalbose yra galimybė išsikviesti funkcijas su vardiniais argumentais. Toks būdas leidžia nurodyti argumentą už tam tikrą parametrą susiejant jį su parametro vardu, o ne su pozicija. Tai galima atlikti, pavyzdžiui, C# arba Python kalbose.
Peržvelkime pavyzdį naudojant vardinius argumentus Python kalboje:
#apskaičiuoti gretasienio tūrį #jei vertė nenurodyta, tai skaitome, kad ji lygi vienam def volume(length=1, width=1, height=1): return length * width * height; print(volume()) # V = 1 print(volume(length=2)) # V = 2 print(volume(length=2, width=3)) # V = 6 print(volume(length=2, width=3, height=4)) # V = 24
Pateiktame pavyzdyje viena ir ta pati funkcija iškviečiama su skirtingais argumentais. Ir matoma koks parametras ir kokiu pavadinimu
inicijuojamas. Jeigu funkcijoje yra parametras, kurio reikšmes galima palikti pagal nutylėjimą, tai yra labai patogu inicijuoti tik reikalingus parametrus naudojant pavadintus argumentus. Bet C programavimo kalboje funkcijų argumentai yra susieti su pozicija, todėl kūrėjui reikia įsiminti parametrų eilės tvarką, kas gali būti tikrai nepatogu jei jų yra daug.
Žemiau parodysime, kaip galima imituoti vardinių argumentų naudojimą C programavimo kalboje.
Mažiau žodžių - daugiau kodo
Pats paprasčiausias sprendimo būdas - perduoti funkcijai ne rinkinį parametrų fragmentų, o struktūrą. Inicijuoti jąją žymiai patogiau. Pavyzdžiui:
#includetypedef struct { int length, width, height; } params_s; int volume_f(params_s in) { return in. length * in. width * in.height ; } int main() { params_s p = {.length=8, .width=4, .height=2}; /* Volume1 = 64 */ printf("Volume1 = %i\n", volume_f(p)); /* Volume2 = 0 */ printf("Volume2 = %i\n", volume_f( (params_s){.width=4, .height=2}) ); return 0; }
Viskas pasidarė paprasčiau, bet vis tiek išliko problema su parametrais pagal nutylėjimą, jei jie skiriasi nuo nulio. Pavyzdžiui aukščiau Volume2=0, nes laukelyje length pagal nutylėjimą inicijuojamas nulis. Taip pat vadindami argumentus vardais mes mokame tuo, kad turime sukurti struktūrą arba prisiminti jos vardą, jei darome tipų priskyrimą. O ir visad daryti tipų priskyrimą nepatogu. Bet į pagalbą atkeliauja...
Skirtingos makrokomandos
Makrokomandos, kurios priima kintamą argumentų skaičių atsirado C kodo standarte: C99. Deklaruojamos jos taip pat, kaip ir funkcija, kuri priima kintamą argumentų skaičių: reikia pridėti daugtaškį paskutinio argumento pabaigoje. Identifikatorius __VA_ARGS__ užsiima argumentais perduotais daugtaškiu, įskaitant kableliais (kabliataškiais) tarp jų. Pavyzdys žemiau:
#include#define printArray(str, ...) { \ double d[] = {__VA_ARGS__, 0} ; \ puts(str); \ for(int i = 0; d[i] !=0; i++) \ printf("%g ", d[i]); \ puts(""); \ } #define DO(...){ __VA_ARGS__ } int main() { printArray("cool array: ", 1, 2, 3, 4, 5); /* обратите внимание, что функции перечислены через точку с запятой */ DO(puts("hello"); puts("world"); return 0); return 0; }
Po pradinio apdorojimo, makrokomandos atsiskleis tokiame kode :
int main() { { double d[] = {1, 2, 3, 4, 5, 0} ; /*...*/}; { puts("hello"); puts("world"); return 0;}; return 0; }
Naudojant skirtingas makrokomandas mes galime iš anksto inicijuoti struktūrą, o vėliau pridėti tai, kas buvo perduota joje.
Rezultatas
Dabar, apjungę visas dalis, galime teigti, kad C programavimo kalboje taip pat yra galimybė iškviesti funkciją perdavus jos vardinius argumentus.
Taigi galiausiai gavome tokį kodą:
Taigi galiausiai gavome tokį kodą:
#includetypedef struct { int length, width, height; } params_s; int volume_f(params_s in) { return in. length * in.width * in.height ; } #define volume(...) \ volume_f((params_s){.length=1, .width=1, .height=1 , __VA_ARGS__}) int main() { printf("volume(): %i\n", volume()); printf("volume(.length = 2): %i\n", volume(.length =2 )); printf("volume(.length = 2, .width = 3): %i\n", volume(.length = 2, .width = 3)); printf("volume(.length = 2, .width = 3, .height =4): %i\n", volume(.length =2, .width =3, .height =4)); }
Visi pavyzdžiai kompiliuojami su -std=c99 arba -std=gnu99 . Be šių kompiliavimo vėliavėlių (flags) kompiliatorius išmeta įspėjimą:
warning: initialized field overwritten [-Woverride-init] clang: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides].
Jeigu norime įspėjimus išjungti, naudojame atitinkamas kompiliavimo vėliavėles (flags): -Wno-initializer-overrides jei clang ir -Wno-override-init jei gcc.
Autoriaus teksto idėja pasiremta knyga: Ben Klemens "21st Century C"
Pasiremta: habrahabr.ru
Komentarų nėra:
Rašyti komentarą