[PL] FAQ: C++ funkcje wariadyczne (va_arg, etc) (last update: 2013-06-02, created: 2013-05-14) back to the list ↑
|
|||
[FAQ - pytania (które dostaje via e-mail/IRC/jabber/etc, oraz na które natrafiam na forach) + moje odpowiedzi]
Poniżej moja odpowiedź na e-mail z prośbą o wyjaśnienie funkcji wariadycznych (variadic) w C++ (to te z ... w prototypie - z nieokreśloną liczbą parametrów opcjonalnych, np. printf). === email === ... W sumie funkcje wariadyczne są z jednej strony bardzo proste, a z drugiej jest trochę zabawy w C/C++ żeby taką sensownie napisać. OK, w skrócie - funkcja wariadyczna to funkcja której można podać nie-stałą liczbę parametrów (tj. raz można taką wywołać z 3ma argumentami, raz z 5cioma, itd). Zapewne kojarzysz funkcję printf - jak wiesz printf przyjmuje od jednego parametru (musi przyjmować jeden), do w zasadzie nieograniczonej ich liczby. Np. printf("jakis tekst\n"); Jeśli chodzi o konstrukcje takowych funkcji, to jest to też w miarę proste, przy czym jest jedna spora różnica w stosunku do normalnych funkcji - do "opcjonalnych" parametrów nie da się bezpośrednio (po nazwie) odwołać - zamiast tego trzeba korzystać z zestawu makr. Żeby było zabawniej, z poziomu funkcji nie wiadomo ile tych opcjonalnych funkcji się dostało (nie jest to nigdzie implicit przekazywane). Więc trzeba sobie to samemu przekazać, implicit bądź explicit. Zauważ, że np. w printf ilość opcjonalnych parametrów można wywnioskować "licząc" ilość %cośtam w stringu z pierwszego parametru. Alternatywnie można we własnej funkcji zdeklarować ilość parametrów opcjonalnych w pierwszym parametrze. Trochę przykładowego kodu - funkcja sumująca podane liczby (w pierwszym parametrze jest ilość liczb, potem są kolejne liczby jako int'y). (do samych opcjonalnych parametrów odwołujesz się zestawem makr z <cstdarg>: va_start, va_end, va_arg; tu jest dobry opis: http://www.cplusplus.com/reference/cstdarg/) #include <cstdio> Jeśli chodzi o problemy z powyższym, to jest kilka: 1. Pamiętaj, że nie masz określonej ilości parametrów opcjonalnych, a więc trzeba przekazać ich ilość explicit, albo chociaż implicit jak w printf jest. 2. Niektóre typy zachowują się "dziwnie" jeśli je przekażesz jako opcjonalny argument. Przykładowo, typ float jest zmieniany na double (a więc w va_arg musisz pamiętać, żeby zawsze używać double jako drugi parametr, nawet jeśli przekazałaś float'a). 3. Ponieważ va_arg nie zna typu, i ten tym mu explicit w funkcji "hardkodujesz" (ustawiasz na stałe), to musisz dbać o to, żeby przy wywołaniu danej funkcji zawsze wywoływać ją z dobrym typem parametrów. Np. funkcji sumuj() wyżej nie możesz podać floatów czy double, czy stringów - zwróci bzdury w takim wypadku i nie zadziała poprawnie. (zauważ, że jeśli chodzi o printf to literka po % mówi jaki jest typ kolejnego parametru, więc w printf wewnętrznie jest switch/case który dla różnych literek wywołuje va_arg z różnymi typami) 4. Te "trzy kropki" (oznaczające opcjonalne parametry) w prototypie muszą być ostatnim argumentem. Nie można stworzyć funkcji w której parametry opcjonalne są w środku (w końcu skoro nie wiadomo ile ich jest, to trudno kompilatorowi wygenerować kod który odwołuje się do czegoś co jest po nich). 5. W C++11 (najnowszy standard C++) są jeszcze inne metody, żeby osiągnąć tego typu efekt (np. std::initializer_list). ... === koniec email === Oczywiście powyższe jest w pewnym uproszczeniu (tj. bez wnikania w wew. działania tego mechanizmu). | |||
|