Nieskończenie wiele argumentów...
W ramach oderwania się od opisywania sposobu komuminakcji z silnikiem Matlaba, opiszę dziś dość niestandardowe rozwiązanie, które jest wprawdzie realizowane przez niektóre funkcje z biblioteki standardowej ANSI C (printf, scanf), jednak nigdy jeszcze się na nie nie natknąłem w napisanym przez kogoś kodzie: funkcje realizujące nieznaną liczbę zmiennych.
Obsługę tego typu funkcji realizuje plik nagłówkowy stdarg.h (w C++ również cstdarg), który definiuje makra umożliwiające dostęp do indywidualnych argumentów znajdujących się na przekazanej do funkcji liście.
Deklaracja funkcji, która może przyjąć dowolną liczbę argumentów jest niezwykle intuicyjna:
int foo( int bar, ... );
Nie jest to żaden skrót myślowy, który postanowiłem zastosować zastosować. Po deklaracji pewnej liczby argumentów, które posiadają zdefiniowane typy i nazwy, następuje znak wielokropka (C++ pozwala przy tym na deklarację takiej funkcji bez potrzeby określania argumentów przed wielokropkiem).
Do obsługi argumentów tej funkcji służą następujące funkcje (makra) i typy:
va_list void va_start( va_list args, aNazwa )
void va_end( va_list args )
typ va_arg( va_list args, typ )
va_list
jest typem, który określa listę argumentów funkcji. Do
zainicjalizowania zmiennej typu va_list
służy makro va_start
, którego wymaga
określenia zadeklarowanej listy typu va_list
oraz nazwy ostatniego nazwanego
parametru (występującego tuż przed znakiem wielokropka w czole naszej funkcji).
Po użyciu makra va_start
zawsze należy wywołać w tej samej funkcji, po
zakończeniu pracy z pracy listą argumentów, makro va_end
. Warto zaznaczyć, że
nie jest konieczne wywołanie go po pobraniu wszystkich argumentów; wystarczy
obsłużyć ich część (albo żaden). D uzyskania dostępu do kolejnych elementów
listy argumentów służy makro va_arg
. Wymaga ono, oprócz listy argumentów,
podania typu, który ma zostać zwrócony (typu, którego jest argument).
W C99 wprowadzono również możliwość kopiowania listy argumentów: void
va_copy(va_list cel, va_list źródło)
. Nie jest to jednak standardem w przypadku
C++.
Niestety, stdlib nie dostarcza żadnych funkcji czy makr umożliwiających
określenie typu lub liczby argumentów. Do określenia typów można iść w ślady
funkcji typu printf
i wymagać podania w pierwszym argumencie pewnych
specyfikatorów określających dalszych argumentów. W celu określeniu ilości
argumentów należy je policzyć: albo poprzez podanie ilości nienazwanych
argumentów w którymś z początkowych argumentów (tych przed wielokropkiem), albo
poprzez stosowanie pewnej wartości (np. NULL), która będzie oznaczać ostatni
argument na liście.
Przykładowa funkcja realizująca powyższy opis wygląda następująco:
// Niech lista argumentów zawiera ciągi znaków
void foo( int count, ... ) {
int i; char* str;
va_list args;
// Inicjalizacja listy argumentów
va_start(args, count);
if( count < 1 )
return;
for( i = 0; i < count; i++ ) {
// Pobieramy kolejny argument
str = va_arg(args,char*);
printf(str);
}
// Koniec pracy z listą argumentów
va_end(args);
return;
}
Napisałem również prosty, gotowy do skompilowania, przykładowy program, który z
kolei wykorzystuje ostatni parametr listy do zakończenia pobierania parametrów.
Parametr ten powinien przyjmować wartość NULL. W ramach ćwiczenia, sprawdź,
drogi Czytelniku, co się stanie gdy w ostatnim wywołaniu funkcji SayHi
usunąć
tego NULLa. :)