Безопасно ли использовать va_list в коде, подверженном ошибкам?

Типичный пример:

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);
}

Это плохая идея, то есть необычно использовать va_list в С++? Если обернуть bar в try-catch, это поможет? Какими будут альтернативы?

Ответ 1

Стандарт С++ отсылает к стандарту C для спецификации va_start et al. Стандарт C имеет это, чтобы сказать:

7.15.1p1... Каждое обращение макросов va_start и va_copy должно соответствовать соответствующему вызову макроса va_end в той же функции.

Таким образом, если вы выходите из функции любыми способами после вызова va_start, но до va_end, ваша программа демонстрирует поведение undefined.

Да, обертка bar в try/catch поможет.

Ответ 2

Стандарт С++ отменил это на стандарт C.

C99 (черновик) 7.15.1/1 говорит нам, что:

Каждое обращение к макросам va_start и va_copy должно соответствовать соответствующий вызов макроса va_end в той же функции.

Таким образом, если bar throw, вы не можете выполнить va_end, а ваша программа имеет поведение undefined. Если вы добавите try/catch, чтобы убедиться, что va_end всегда вызывается как требуется, тогда вы должны быть в порядке. Но помните, что вы не можете передавать не-POD как varargs, поэтому, если вам нужно их обработать, вам все равно нужен альтернативный механизм.

Более альтернативой с С++, вероятно, будут операторы вставки (operator<<), как это видно в различных iostreams, предоставляемых языком.

Ответ 3

Как упоминалось выше, поведение Undefined по стандарту c. Но в зависимости от вашей платформы марко может скомпилироваться в разные вещи, я вижу, например, это просто args = 0; И va_list - это char *; В этом случае кажется, что конечный макрос не делает ничего критического. Ничего плохого не должно произойти, кроме того, что я не уверен, кто отменит аргументы, но я не знаю, где они выделяются в первую очередь.

Я никоим образом не рекомендую использовать это, но иногда сумасшедшие вещи необходимы для поддержки устаревшего кода. Если вы можете использовать try catch, то обязательно сделайте это.