Можно ли использовать Boost.Format с предварительно выделенным буфером? - PullRequest
7 голосов
/ 02 ноября 2011

Мне было интересно, поддерживает ли Boost.Format использование буфера фиксированной ширины / предварительно выделенного в качестве вывода вместо динамического буфера, управляемого самой библиотекой?

То есть обычно вы делаете:

boost::format myfmt("arg1: %1% / arg2: %2%");
// e.g.:
cout << (myfmt % 3.14 % 42);
// or
string s = boost::str( myfmt % "hey!" % "there!");

поэтому библиотека Boost: Format автоматически позаботится о выделении достаточного пространства и управлении «буфером вывода».

Мне было интересно, есть ли способ использовать предопределенный нединамический буфер с Boost.Format, то есть что-то вроде:

const size_t buf_sz = 512;
char big_enough[buf_sz];
boost::format myfmt("arg1: %1% / arg2: %2%");
myfmt.attach_buffer(big_enough, buf_sz);
myfmt % "hey!" % "there!"
// big_enough buffer now contains the result string

Я знаю, что мог бы просто просмотреть примеры, документы и источник, но не считая недостатка времени. (и сама возможность чего-то упустить) было бы интересно узнать: Если это невозможно, было бы здорово, если бы кто-то мог объяснить, почему (если есть / есть конкретные причины) - было ли это преднамеренно? разве это не соответствует API? ...

Отказ от ответственности: Этот вопрос не о производительности!

1 Ответ

5 голосов
/ 03 ноября 2011

Начальная идея

Глядя на источник , кажется, что вы можете использовать свой собственный распределитель, который затем используется внутренним потоком (internal_streambuf_t) boost::format. Это было бы достаточно для вашего случая?

Например, вы можете использовать что-то вроде libstdc ++ array_allocator

К сожалению boost::format также использует пару std::vector, которые не используют пользовательский распределитель, что может быть проблемой в вашем случае?

Как boost::format работает

Я посмотрел на источник boost::format, и вот как он работает (ниже описан str(), << вызывает либо str() или использует стандартные std::ostream вещи):

  • класс формата хранит все аргументы и строку формата отдельно, иногда используя пользовательский распределитель, иногда используя распределитель по умолчанию
  • когда вызывается str(), он создает новый std::string и делает его достаточно большим для результата, используя пользовательский распределитель
  • затем добавляет все аргументы и фрагменты статической строки из строки формата в строку результата
  • наконец возвращает строку результата по значению

Итак, итоговая строка результата не сохраняется внутри класса формата, а создается при необходимости.

Таким образом, даже если вы можете найти местоположение строки результата при использовании пользовательского распределителя, она доступна только после / во время вызова str(). Это должно объяснить, почему это невозможно: отформатированный результат никогда не сохраняется в «выходном буфере» в классе.

Почему это так работает

Почему они так поступили, я не знаю. Я думаю, это потому, что вы можете построить результат только после того, как все аргументы известны, это тратит пространство на хранение результата, и вам, вероятно, нужен только один раз для данной комбинации формат / аргумент. Поэтому создание его при необходимости не приводит к дополнительной работе, поскольку обычно str() вызывается только один раз.

Решения

  • Создайте оболочку вокруг str() или << и скопируйте результат в свой фиксированный буфер
  • Используйте stream_buffer для потоковой передачи строки в буфер (см. Пример ниже)
  • Унаследуйте класс и добавьте свою собственную функцию str(), которая сохраняет результат в фиксированном буфере.

Возможное решение с использованием boost :: iostreams (протестировано):

#include <iostream>
#include <boost/format.hpp>
#include <boost/iostreams/stream.hpp>

int main()
{
    char buffer[100];

    boost::iostreams::stream<boost::iostreams::array_sink>
        stream(buffer, sizeof(buffer));

    stream << (boost::format("arg1 = %1%") % 12.5);
    stream << '\0';  // make sure buffer contains 0-terminated string

    std::cout << buffer << std::endl;    
}
...