2013-07-18 10 views
11

Typowy przykład:Czy można bezpiecznie używać listy va_list w podatnym na wyjątki kodzie?

void foo(const char *fmt, ...) 
{ 
    va_list args; 
    va_start(args, fmt); 

    // might throw, might not. who knows. 
    bar(fmt, args); 

    // uh-oh... 
    va_end(args); 
} 

Czy to zły pomysł, to jest to rzadkością, aby korzystać va_list w C++? Jeśli zapakuję bar w try-catch, czy to pomoże? Jakie byłyby alternatywy?

+0

Listy parametrów zmiennej długości IMHO są złym pomysłem. Spójrz na model 'iostream' dla rozsądnego rozwiązania. –

+0

Po prostu zapakuj w try/catch i powinno być "bezpieczne". – jmucchiello

+0

@EdHeal Listy parametrów zmiennej długości są w porządku, o ile są one implementowane przy użyciu szablonów variadic. –

Odpowiedz

4

Standard C++ odwołuje się do standardu C dla specyfikacji va_start i in. Standard C ma to na myśli:

7.15.1p1 ... Każde wywołanie makr va_start i va_copy zostanie dopasowane przez odpowiednie wywołanie makra va_end w tej samej funkcji.

W ten sposób, po wyjściu z funkcji w dowolny sposób po wywołaniu va_start, ale przed va_end, twój program wykazuje niezdefiniowane zachowanie.

Tak, opakowanie bar w try/catch może pomóc.

+0

Jest to oczywiście w 100% prawdziwe i nie jestem zwolennikiem pisania kodu, który zakłada konkretną implementację, ale chciałem po prostu wyrzucić, że wszystkie trzy implementacje, które widziałem dla 'va_end' są czymś podobnym do' ap = (va_list) 0; ', tj. null na liście. Więc nie wywoływanie 'va_end' spowodowałoby technicznie UB, ale w praktyce prawdopodobnie nie. Ponownie, nie zalecając pisania niepoprawnego kodu tutaj, a próbka o wielkości trzech nie jest ogromna.Standard musi określać, jak używać 'va_list' i powiązanych z nim funkcji, ale w praktyce prawdopodobnie to nie ma znaczenia. –

3

Standard C++ odwołuje to do standardu C.

C99 (draft) 7.15.1/1 mówi nam, że:

Każde wywołanie tej va_start i va_copy makra powinny być dopasowane przez odpowiadać wywołanie makra va_end w tej samej funkcji.

Zatem jeśli bar rzuca, nie uda się wykonać va_end a program niezdefiniowane zachowanie. Jeśli dodasz try/catch, aby upewnić się, że va_end jest zawsze wywoływana jako wymagana, powinieneś być w porządku. Ale pamiętaj, że nie możesz przekazywać nie-POD jako varargs, więc jeśli potrzebujesz ich obsługiwać, i tak będziesz potrzebował alternatywnego mechanizmu.

Bardziej podobną do C++ alternatywą będą prawdopodobnie operatory wstawiania (operator<<), jak widać w różnych wersjach językowych dostarczonych przez ten język.

+9

... lub szablony variadyczne C++ 11. Wtedy nie potrzebujemy już strasznego hakerskiego operatora. –

0

Jak wspomniano powyżej, jest niezdefiniowanym zachowaniem według standardu c. Jednak w zależności od platformy marco może się kompilować w różne rzeczy, widzę dla mnie na przykład po prostu robi args = 0; A va_list to char *; W takim przypadku wydaje się, że makro końcowe nie robi nic krytycznego. Nic złego nie powinno się stać, z wyjątkiem nie jestem pewien, kto zwolni te argumenty, ale nie wiem, gdzie są one przydzielone w pierwszej kolejności.

W żaden sposób nie polecam używania tego, ale czasami szalone rzeczy są niezbędne do wspierania starszego kodu. Jeśli możesz spróbować złapać tam, to zrób to.

Powiązane problemy