Проверено, ответ качества продукции
Этот ответ обрабатывает общий случай с помощью методов, соответствующих стандартам. Тот же подход приведен в качестве примера на CppReference.com в нижней части их страницы. В отличие от их примера, этот код соответствует требованиям вопроса и испытан в полевых условиях в робототехнике и спутниковых приложениях. Это также улучшило комментирование. Качество дизайна обсуждается ниже.
#include <string>
#include <cstdarg>
#include <vector>
// requires at least C++11
const std::string vformat(const char * const zcFormat, ...) {
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
return std::string(zc.data(), iLen); }
#include <ctime>
#include <iostream>
#include <iomanip>
// demonstration of use
int main() {
std::time_t t = std::time(nullptr);
std::cerr
<< std::put_time(std::localtime(& t), "%D %T")
<< " [debug]: "
<< vformat("Int 1 is %d, Int 2 is %d, Int 3 is %d", 11, 22, 33)
<< std::endl;
return 0; }
Предсказуемая линейная эффективность
Два прохода необходимы для безопасной, надежной и предсказуемой функции многократного использования согласно спецификациям вопроса. Предположение о распределении размеров vargs в многократно используемой функции является плохим стилем программирования и его следует избегать. В этом случае произвольно большие представления переменной переменной vargs длины являются ключевым фактором при выборе алгоритма.
Повторная попытка переполнения является экспоненциально неэффективной, что является еще одной причиной, обсуждаемой, когда комитет по стандартам C ++ 11 обсуждал вышеупомянутое предложение, чтобы обеспечить пробный запуск, когда буфер записи равен нулю.
В приведенной выше реализации готового производства первый прогон является таким пробным прогоном для определения размера распределения. Распределение не происходит. Парсинг директив printf и чтение vargs были сделаны чрезвычайно эффективными в течение десятилетий. Повторно используемый код должен быть предсказуемым, даже если нужно пожертвовать небольшой неэффективностью для тривиальных случаев.
Безопасность и надежность
Эндрю Кениг сказал небольшой группе из нас после своей лекции на мероприятии в Кембридже: «Функции пользователя не должны полагаться на использование отказа из-за исключительной функциональности». Как обычно, его мудрость была показана в записи с тех пор. Исправленные и закрытые ошибки безопасности часто указывают на повторные попытки в описании дыры, использованной до исправления.
Это упоминается в официальном предложении о пересмотре стандартов для функции нулевого буфера в Альтернатива sprintf, Предложение по пересмотру C9X , Документ ИСО МЭК WG14 N645 / X3J11 96-008 . Строка произвольной длины, вставленная в директиву печати «% s» в рамках ограничений доступности динамической памяти, не является исключением и не должна использоваться для создания «Необычайной функциональности».
Рассмотрим предложение вместе с примером кода, приведенным внизу страницы C ++ Reference.org, на которую есть ссылка в первом абзаце этого ответа.
Кроме того, тестирование случаев отказа редко бывает таким же успешным.
Портативность
Все основные О.С. поставщики предоставляют компиляторы, которые полностью поддерживают std :: vsnprintf как часть стандартов c ++ 11. Хосты, использующие продукты поставщиков, которые больше не поддерживают дистрибутивы, должны быть снабжены g ++ или clang ++ по многим причинам.
Использование в стеке
Использование стека при первом вызове std :: vsnprintf будет меньше или равно использованию второго, и оно будет освобождено до начала второго вызова. Если первый вызов превысит доступность стека, то std :: fprintf тоже не удастся.