Безопасно ли использовать 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, то обязательно сделайте это.