Powiedzmy, że mam funkcję C, która pobiera zmienną liczbę argumentów: Jak mogę wywołać inną funkcję, która oczekuje od niej zmiennej liczby argumentów, przekazując wszystkie argumenty, które dostały się do pierwszej funkcji?
Przykład:
void format_string(char *fmt, ...);
void debug_print(int dbg_lvl, char *fmt, ...) {
format_string(fmt, /* how do I pass all the arguments from '...'? */);
fprintf(stdout, fmt);
}
c
variadic-functions
Vicent Marti
źródło
źródło
Odpowiedzi:
Aby przekazać elipsy, musisz przekonwertować je na listę va_list i użyć tej listy va w swojej drugiej funkcji. Konkretnie;
źródło
format_string
również nie będzie to przydatne, ponieważ musiałoby to modyfikować fmt in situ, czego z pewnością nie należy robić. Opcje obejmowałyby całkowite pozbycie się format_string i użycie vfprintf, ale to czyni założenia o tym, co faktycznie robi format_string, lub format_string zwraca inny ciąg. Przeredaguję odpowiedź, aby pokazać to drugie.argptr
a wywoływana funkcja używaargptr
w ogóle, jedyną bezpieczną rzeczą do zrobienia jest wywołanie,va_end()
a następnie ponowne uruchomienie wva_start(argptr, fmt);
celu ponownej inicjalizacji. Lub możesz użyć,va_copy()
jeśli twój system go obsługuje (C99 i C11 tego wymagają; C89 / 90 nie wymaga).Nie ma możliwości wywołania (np.) Printf bez wiedzy, ile argumentów mu przekazujesz, chyba że chcesz dostać się do niegrzecznych i nieprzenośnych sztuczek.
Powszechnie stosowanym rozwiązaniem jest, aby zawsze zapewnić alternatywną postać funkcji vararg, więc
printf
mavprintf
co zajmujeva_list
w miejsce...
. The...
wersje są tylko obwolut wokółva_list
wersjach.źródło
vsyslog
jest zgodny z POSIX .Funkcje Variadic mogą być niebezpieczne . Oto bezpieczniejsza sztuczka:
źródło
#define callVardicMethodSafely(values...) ({ values *v = { values }; _actualFunction(values, sizeof(v) / sizeof(*v)); })
We wspaniałym C ++ 0x możesz użyć różnych szablonów:
źródło
Można użyć złożenia wbudowanego do wywołania funkcji. (w tym kodzie zakładam, że argumentami są znaki).
źródło
Możesz także spróbować makra.
źródło
Chociaż można rozwiązać przekazanie formatera, najpierw przechowując go w buforze lokalnym, ale wymaga to stosu i może czasem być problem. Próbowałem śledzić i wydaje się, że działa dobrze.
Mam nadzieję że to pomoże.
źródło
Rozwiązanie Rossa zostało trochę oczyszczone. Działa tylko wtedy, gdy wszystkie argumenty są wskaźnikami. Również implementacja języka musi obsługiwać wyświetlanie poprzedniego przecinka, jeśli
__VA_ARGS__
jest pusty (zarówno Visual Studio C ++, jak i GCC do).źródło
Załóżmy, że masz typową funkcję wariadyczną, którą napisałeś. Ponieważ przed argumentem variadycznym wymagany jest co najmniej jeden argument
...
, zawsze musisz napisać dodatkowy argument w użyciu.Czy ty
Jeśli zawijasz funkcję variadic w makrze, nie potrzebujesz poprzedzającego argumentu. Rozważ ten przykład:
Jest to oczywiście znacznie wygodniejsze, ponieważ nie trzeba za każdym razem określać początkowego argumentu.
źródło
Nie jestem pewien, czy to działa dla wszystkich kompilatorów, ale do tej pory działało dla mnie.
Możesz dodać ... do inner_func (), jeśli chcesz, ale nie potrzebujesz. Działa, ponieważ va_start używa adresu danej zmiennej jako punktu początkowego. W tym przypadku podajemy mu odwołanie do zmiennej w func (). Używa więc tego adresu i odczytuje zmienne później na stosie. Funkcja inner_func () odczytuje z adresu stosu func (). Działa to tylko wtedy, gdy obie funkcje używają tego samego segmentu stosu.
Makra va_start i va_arg będą na ogół działać, jeśli podasz im dowolny var jako punkt początkowy. Więc jeśli chcesz, możesz przekazać wskaźniki do innych funkcji i również z nich korzystać. Możesz łatwo tworzyć własne makra. Wszystkie makra to adresy pamięci typecast. Jednak zmuszanie ich do działania dla wszystkich kompilatorów i konwencji wywoływania jest denerwujące. Ogólnie łatwiej jest korzystać z tych, które są dostarczane z kompilatorem.
źródło