Не думаю, что вы можете делать то, что ищете (кроме простого способа перераспределения буфера до необходимого размера и повторного выполнения всей операции).
Причины, которые вы перечислили, являются причиной этого, но настоящий убийца заключается в том, что средство форматирования могло занимать форматирование аргумента, когда ему не хватило места, и разумного способа перезапустить его нет.
Например, допустим, что в буфере осталось 3 байта, и средство форматирования начинает работать с преобразованием "% d" для значения -1234567
. Он поместит «-1 \ 0» в буфер, а затем сделает все остальное, что нужно сделать, чтобы вернуть размер буфера, который вам действительно нужен.
В дополнение к тому, что вы можете определить, над каким спецификатором работал форматировщик, вам необходимо выяснить, что вместо ввода -1234567
во втором раунде вам нужно передать 234567
. Я бросаю вам вызов, чтобы придумать разумный способ сделать это.
Теперь, если есть реальная причина, по которой вы не хотите перезапускать операцию сверху, вы, вероятно, могли бы обернуть вызов snprintf()
/ vsnprintf()
чем-то, что разбивает строку формата, отправляя только один спецификатор преобразования за один раз и объединяя этот результат в выходной буфер. Вам нужно было бы придумать какой-нибудь способ, чтобы оболочка могла сохранять некоторое состояние при повторных попытках, чтобы она знала, какую спецификацию конвертации выбрать.
Так что, возможно, это в некотором смысле выполнимо, но, похоже, кажется, что было бы ужасно много работы, чтобы избежать гораздо более простой схемы «полного повтора». Я мог видеть, может быть (возможно), пробовать это в системе, где у вас нет роскоши динамически распределять больший буфер (встроенная система, может быть). В этом случае я бы, вероятно, утверждал, что нужен гораздо более простой / ограниченный форматер области видимости, который не обладает всей гибкостью форматеров printf()
и может обрабатывать повторные попытки (поскольку их область действия более ограничена).
Но, мужик, я бы очень старался вразумить того, кто сказал, что это требование.
Edit:
На самом деле, я забираю часть этого обратно. Если вы хотите использовать настроенную версию snprintf()
(назовем ее snprintf_ex()
), я могу увидеть, что это относительно простая операция:
int snprintf_ex( char* s, size_t n, size_t skipChars, const char* fmt, ...);
snprintf_ex()
(и его сопутствующие функции, такие как vsnprintf()
) форматируют строку в предоставленный буфер (как обычно), но пропускают вывод первых skipChars
символов.
Вероятно, вы могли бы довольно легко настроить это, используя исходный код из библиотеки вашего компилятора (или используя что-то вроде Holger Weiss 'snprintf()
) в качестве отправной точки. Использование этого может выглядеть примерно так:
int bufSize = sizeof(buf);
char* fmt = "some complex format string...";
int needed = snprintf_ex( buf, bufSize, 0, fmt, arg1, arg2, etc, etc2);
if (needed >= bufSize) {
// dang truncation...
// do whatever you want with the truncated bits (send to a logger or whatever)
// format the rest of the string, skipping the bits we already got
needed = snprintf_ex( buf, bufSize, bufSize - 1, fmt, arg1, arg2, etc, etc2);
// now the buffer contains the part that was truncated before. Note that
// you'd still need to deal with the possibility that this is truncated yet
// again - that's an exercise for the reader, and it's probably trickier to
// deal with properly than it might sound...
}
Один недостаток (который может или не может быть приемлемым) состоит в том, что средство форматирования будет снова выполнять всю работу по форматированию с самого начала - оно просто отбрасывает первые skipChars
символов, которые оно встречает. Если бы мне пришлось использовать что-то подобное, я бы подумал, что это почти наверняка будет приемлемо (то, что происходит, когда кто-то имеет дело с усечением, используя стандартное семейство функций snprintf()
).