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. :)