2009-03-30 11 views
60

Mam około 30 funkcji variadycznych. Każdy przyjmuje ścieżki jako argumentu końcowego, np .:Przekazywanie elipsy do innej funkcji variadycznej

bool do_foo(struct *f, int q, const char *fmt, ...) 

W każdej funkcji, trzeba sprawdzić, czy rozszerzonego formatu jest mniejsza niż lub równa pewnej wielkości. Tak więc, znajduję sobie kopię/wklejenie tego samego kawałka kodu, aby sprawdzić, ile znaków nie zostało wydrukowanych przez vsnprintf(), odpowiednio ustaw errno i wyskocz z zapisu.

Co chciałbym zrobić, to napisać funkcję, która to zrobi, która zwróci statycznie przydzielony (rozwinięty) ciąg, który jest znany jako bezpieczny rozmiar, lub nowo zainicjowany ciąg w przypadku niepowodzenia, który może być sprawdzony pod kątem NULL . Kontrole muszą również określić, czy łańcuch jest ścieżką bezwzględną lub względną, co wpływa na bezpieczny rozmiar ciągu znaków. To dużo duplikatu kodu i zaczyna pachnieć.

Czy istnieje sposób, że mogę przekazać zawartość elipsis z wejścia mojej funkcji do innej funkcji? Czy muszę najpierw zadzwonić pod numer va_start(), a następnie przekazać va_list do funkcji pomocnika?

Edit:

nie jestem w ogóle przeciwny minięciu va_list do pomocnika, po prostu chciałem się upewnić, że nic innego nie istniało. Wydaje mi się, że kompilator rozumie, gdzie zaczynają się argumenty variadyczne, więc byłam ciekawa, czy mogę powiedzieć, żeby przekazać je dalej.

+0

Czy jest jakiś powód, dla którego są niekorzystne dla minięciu va_list do pomocnika? – ojblass

+0

Musiałem robić podobne rzeczy, ale musiałem pozbyć się kilku argumentów ... nie był przyjemny kod do utrzymania. – ojblass

Odpowiedz

63

Nie możesz, możesz przekazywać argumenty tylko jako va_list. Zobacz comp.lang.c FAQ.

W ogóle, jeśli piszesz funkcji o zmiennej liczbie argumentów (czyli funkcje, które podjąć zmienną liczbę argumentów) w C, należy napisać dwie wersje każdej funkcji: jeden, który trwa wielokropek (...) i jedno który zajmuje va_list. Wersja pobierająca wielokropek powinna zadzwonić pod numer va_start, zadzwonić pod wersję va_list, zadzwonić pod numer va_end i wrócić. Nie ma potrzeby powielania kodu między dwiema wersjami funkcji, ponieważ jedna wywołuje drugą.

+5

Byłoby miło, gdybyś napisał kod. –

+0

Co należy zrobić, jeśli muszę dwukrotnie wykonać 'va_list'? Zwykle musiałbym wywoływać 'va_end' i' va_start' między użytecznościami (inaczej jest to UB), ale nie mogę tego wywołać w funkcji, która pobiera 'va_list':' 'va_start 'użytą w funkcji ze stałymi args'' . –

+1

Nieważne, zorientowałem się - potrzebuję użyć 'va_copy' dla każdego kolejnego użycia. –

0

Musisz przekazać va_list do pomocy.

+1

Ta odpowiedź jest sposobem na zwięzłość, aby była przydatna. Powinien być rozszerzony o dodatkowe przydatne szczegóły lub usunięty. – rjstelling

+0

@rjstelling Znalazłem tę odpowiedź przydatną, gdy zadałem pytanie.Jeśli zauważysz, wskazałem, że zasadniczo zgodziłem się na przekazanie va_list, ale czułem, że może być inny sposób robienia tego. Andrew prawie potwierdził, że nie było żadnego (nie mogłem użyć magii kompilatora). Podczas gdy inne odpowiedzi pogłębiły się, w wystarczającym stopniu odpowiadałem na moje pytanie. –

9

Prawdopodobnie można użyć zmiennej liczbie argumentów makra - tak:

#define FOO(...) do { do_some_checks; myfun(__VA_ARGS__); } while (0) 

NB! Makra Variadic są tylko C99

+0

Patrzyłem na te, które oszczędzają mi konieczności dodawania otoki wokół funkcji, która faktycznie robi zapis. –

+0

c99 nie jest problemem, mój program jest raczej gcc/linux specyficzny –

-1

Nie wiem, czy to pomoże, możesz uzyskać dostęp do zmiennych przez odniesienie. Jest to rodzaj podstępnej sztuczki, ale niestety nie pozwoli ci użyć elipsy w ostatecznej definicji funkcji.

#include <stdio.h> 

void print_vars(int *n) 
{ 
    int i; 
    for(i=0;i<=*n;i++) 
    printf("%X %d ", (int)(n+i), *(n+i)); 
    printf("\n"); 
} 

void pass_vars(int n, ...) 
{ 
    print_vars(&n); 
} 

int main() 
{ 
    pass_vars(4, 6, 7, 8, 0); 
    return 0; 
} 

na moim komputerze wyprowadza

$ ./a.out 
BFFEB0B0 4 BFFEB0B4 6 BFFEB0B8 7 BFFEB0BC 8 BFFEB0C0 0 
+9

Nie jest przenośny ... – aschepler

Powiązane problemy