Создает ли `basic_streambuf` свои собственные области get/put, если вы не делаете этого в реализации производного класса?

Я много раз видел инструкции по созданию пользовательских потоковых буферов: все, что вам нужно сделать, это правильно реализовать overflow, underflow и pbackfail в потомке std::basic_streambuf, и вы сможете создать поток, который форматирует данные, используя его. Эти три подпрограммы определяют «управляемую последовательность» вашего пользовательского потока.

Но в списке защищенных членов std::basic_streambuf скрываются и другие монстры, а именно setg и setp. Они устанавливают буферные области для ввода и вывода. Публичные члены, которые получают и устанавливают данные, сначала пытаются получить доступ к этим областям, прежде чем перейти к контролируемой последовательности.

Для пары различных пользовательских потоковых буферов могут возникнуть проблемы, если поток устанавливает свои собственные области получения/ввода. Поэтому я хотел бы, чтобы такие потоковые буферы избегали использования областей get/put и всегда использовали overflow, underflow и pbackfail без какой-либо промежуточной буферизации.

Для наивного упрощенного примера, если вы оборачиваете другой streambuf, реализация underflow может выглядеть так:

template <class C, class TR>
typename TR::int_type wrapping_streambuf<C, TR>::underflow()
{
 return m_wrapped_streambuf->sgetc();
}

Пусть обернутый streambuf сделает всю грязную работу. Вот еще один наивный пример подсчета строк:

template <class C, class TR>
typename TR::int_type tracking_streambuf<C, TR>::uflow()
{
  auto rv = m_wrapped_streambuf->sbumpc();
  if (rv == (TR::int_type)'\n') ++ m_input_line_count;
  return rv;
}

Для таких потоков нет полезной реализации setg, потому что вы не можете получить доступ к внутренней области получения завернутого буфера. Для tracked_streambuf наложение областей get/put сделало бы невозможным подсчет строк в синхронизации с логической последовательностью потока.

Я думаю, что ответ заключается в том, чтобы никогда не вызывать setg или setp в классах-потомках. Фактически, они, вероятно, должны переопределить setg, setp, gbump и pbump, чтобы генерировать исключения.

Глядя на заголовок <streambuf>, я вижу, что пользовательский streambuf в реализации моей любимой библиотеки может работать так, как я хочу (есть проверки на null gptr/pptr), если я это сделаю. Но является ли это гарантией?


person Spencer    schedule 26.01.2018    source источник


Ответы (1)


Конструктор по умолчанию std::basic_streambuf устанавливает для шести указателей, определяющих области получения и помещения, нулевые значения указателя, поэтому по умолчанию он не будет «создавать свою собственную область получения/ввода».

Функции setg, setp, gbump и pbump являются защищенными членами и не будут вызываться общедоступными функциями-членами по умолчанию, поэтому вам не нужно о них беспокоиться. Конечно, переопределить их, чтобы генерировать исключения, неплохо.

Кроме того, пользовательский класс буфера потока без промежуточного буфера также должен переопределять uflow, которая может вызываться общедоступными функциями-членами для обработки случаев переполнения, когда необходимо увеличить значение указателя get. По умолчанию его поведение по умолчанию (цитируется по [streambuf.virt. получить]/16):

Поведение по умолчанию: вызывает underflow(). Если underflow() возвращает traits​::​eof(), возвращает traits​::​eof(). В противном случае возвращает значение traits​::​to_­int_­type(*gptr()) и увеличивает значение следующего указателя для входной последовательности.

Поэтому, если вы не переопределите эту функцию, это приведет к неопределенному поведению для косвенности через нулевой указатель.

person xskxzr    schedule 26.01.2018
comment
Действительно хороший ответ и хорошее замечание о uflow. Похоже, вы цитируете Стандарт (вы использовали слово «должен» вместо «должен»). Можете ли вы обновить номер соответствующего раздела, даже если это ненормативное примечание? - person Spencer; 26.01.2018
comment
@Spencer Стандарт явно не предлагает переопределять uflow (по крайней мере, я не нашел), в то время как cppreference page делает. Но вы правы, моя привычка использовать должен исходить из формулировки стандарта. :) - person xskxzr; 26.01.2018
comment
Просто на заметку: я изменил get/set на get/put. - person Spencer; 27.01.2018