Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Direct iostream formatting: vectorstream and bufferstream

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 16. Boost.Interprocess

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Общая память, файлы с картой памяти и все механизмы Boost.Interprocess ориентированы на эффективность. Причина, по которой используется общая память, заключается в том, что это самый быстрый доступный механизм IPC. При передаче текстовых сообщений через общую память необходимо форматировать сообщение. Очевидно, что C++ предлагает iostream-фреймворк для этой работы.

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

//Some formatting elements
std::string my_text = "...";
int number;
//Data reader
std::istringstream input_processor;
//This makes a copy of the string. If not using a
//reference counted string, this is a serious overhead.
input_processor.str(my_text);
//Extract data
while(/*...*/){
   input_processor >> number;
}
//Data writer
std::ostringstream output_processor;
//Write data
while(/*...*/){
   output_processor << number;
}
//This returns a temporary string. Even with return-value
//optimization this is expensive.
my_text = input_processor.str();

Проблема еще хуже, если строка является строкой с общей памятью, потому что для извлечения данных мы должны сначала скопировать данные из общей памяти в std::string, а затем в std::stringstream. Для кодирования данных в строке общей памяти мы должны скопировать данные из строки std::stringstream в строку std::string, а затем в строку совместно используемой памяти.

vectorstream и bufferstream iostreams и все отформатирование/локальная тяжелая работа выполняется по стандартным std::basic_streambuf<> и std::basic_iostream<> классам. .vectorstream and bufferstream implement vector-based and fixed-size buffer based storage support for iostreams and all the formatting/locale hard work is done by standard std::basic_streambuf<> and std::basic_iostream<> classes. [ORIG_END] -->

basic_ovectorstream и basic_vectorstream эффективный способ получения отформатированного чтения/писи непосредственно в символе вектор. Таким образом, если используется вектор с общим памятью, данные извлекаются/писаны из/в общий вектор памяти, без дополнительной копии/выделения. Мы Здесь можно увидеть заявление basic_vectorstream:

//!A basic_iostream class that holds a character vector specified by CharVector
//!template parameter as its formatting buffer. The vector must have
//!contiguous storage, like std::vector, boost::interprocess::vector or
//!boost::interprocess::basic_string
template <class CharVector, class CharTraits =
         std::char_traits<typename CharVector::value_type> >
class basic_vectorstream
: public std::basic_iostream<typename CharVector::value_type, CharTraits>
{
   public:
   typedef CharVector                                                   vector_type;
   typedef typename std::basic_ios
      <typename CharVector::value_type, CharTraits>::char_type          char_type;
   typedef typename std::basic_ios<char_type, CharTraits>::int_type     int_type;
   typedef typename std::basic_ios<char_type, CharTraits>::pos_type     pos_type;
   typedef typename std::basic_ios<char_type, CharTraits>::off_type     off_type;
   typedef typename std::basic_ios<char_type, CharTraits>::traits_type  traits_type;
   //!Constructor. Throws if vector_type default constructor throws.
   basic_vectorstream(std::ios_base::openmode mode
                     = std::ios_base::in | std::ios_base::out);
   //!Constructor. Throws if vector_type(const Parameter &param) throws.
   template<class Parameter>
   basic_vectorstream(const Parameter &param, std::ios_base::openmode mode
                     = std::ios_base::in | std::ios_base::out);
   ~basic_vectorstream(){}
   //!Returns the address of the stored stream buffer.
   basic_vectorbuf<CharVector, CharTraits>* rdbuf() const;
   //!Swaps the underlying vector with the passed vector.
   //!This function resets the position in the stream.
   //!Does not throw.
   void swap_vector(vector_type &vect);
   //!Returns a const reference to the internal vector.
   //!Does not throw.
   const vector_type &vector() const;
   //!Preallocates memory from the internal vector.
   //!Resets the stream to the first position.
   //!Throws if the internals vector's memory allocation throws.
   void reserve(typename vector_type::size_type size);
};

Векторный тип является шаблонизированным, так что мы можем использовать любой тип вектора: std::vector, boost::interprocess::vector... Но хранилище должно быть смежным, мы не можем использовать деке. Мы даже можем использовать boost::interprocess::basic_string, поскольку он имеет векторный интерфейс и имеет смежное хранилище. Мы не можем использовать std::string, потому что, хотя некоторые реализации std::string основаны на векторах, другие могут иметь оптимизации и учетные реализации.

Пользователь может получить ссылку const на внутренний вектор, используя функцию vector_type vector()const, а также может поменять внутренний вектор на внешний, называя void swap_vector(vect). Функция swap сбрасывает положение потока. Эти функции позволяют эффективно получать отформатированные данные, избегая всех выделений и копий данных.

Давайте посмотрим пример, как использовать векторный поток:

#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/streams/vectorstream.hpp>
#include <iterator>
using namespace boost::interprocess;
typedef allocator<int, managed_shared_memory::segment_manager>
   IntAllocator;
typedef allocator<char, managed_shared_memory::segment_manager>
   CharAllocator;
typedef vector<int, IntAllocator>   MyVector;
typedef basic_string
   <char, std::char_traits<char>, CharAllocator>   MyString;
typedef basic_vectorstream<MyString>               MyVectorStream;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   managed_shared_memory segment(
      create_only,
      "MySharedMemory", //segment name
      65536);           //segment size in bytes
   //Construct shared memory vector
   MyVector *myvector =
      segment.construct<MyVector>("MyVector")
      (IntAllocator(segment.get_segment_manager()));
   //Fill vector
   myvector->reserve(100);
   for(int i = 0; i < 100; ++i){
      myvector->push_back(i);
   }
   //Create the vectorstream. To create the internal shared memory
   //basic_string we need to pass the shared memory allocator as
   //a constructor argument
   MyVectorStream myvectorstream(CharAllocator(segment.get_segment_manager()));
   //Reserve the internal string
   myvectorstream.reserve(100*5);
   //Write all vector elements as text in the internal string
   //Data will be directly written in shared memory, because
   //internal string's allocator is a shared memory allocator
   for(std::size_t i = 0, max = myvector->size(); i < max; ++i){
      myvectorstream << (*myvector)[i] << std::endl;
   }
   //Auxiliary vector to compare original data
   MyVector *myvector2 =
      segment.construct<MyVector>("MyVector2")
      (IntAllocator(segment.get_segment_manager()));
   //Avoid reallocations
   myvector2->reserve(100);
   //Extract all values from the internal
   //string directly to a shared memory vector.
   std::istream_iterator<int> it(myvectorstream), itend;
   std::copy(it, itend, std::back_inserter(*myvector2));
   //Compare vectors
   assert(std::equal(myvector->begin(), myvector->end(), myvector2->begin()));
   //Create a copy of the internal string
   MyString stringcopy (myvectorstream.vector());
   //Now we create a new empty shared memory string...
   MyString *mystring =
      segment.construct<MyString>("MyString")
      (CharAllocator(segment.get_segment_manager()));
   //...and we swap vectorstream's internal string
   //with the new one: after this statement mystring
   //will be the owner of the formatted data.
   //No reallocations, no data copies
   myvectorstream.swap_vector(*mystring);
   //Let's compare both strings
   assert(stringcopy == *mystring);
   //Done, destroy and delete vectors and string from the segment
   segment.destroy_ptr(myvector2);
   segment.destroy_ptr(myvector);
   segment.destroy_ptr(mystring);
   return 0;
}

Как видно, векторный поток предлагает простой и безопасный способ эффективного форматирования iostream, но во многих случаях нам приходится считывать или записывать отформатированные данные из / в буфер символов фиксированного размера (статический буфер, c-струна или любая другая). Из-за накладных расходов на стрингстрим многие разработчики (особенно во встроенных системах) выбирают семейство sprintf. Классы bufferstream предлагают интерфейс iostream с прямым форматированием в буфере памяти фиксированного размера с защитой от переполнения буфера. Это и есть интерфейс:

//!A basic_iostream class that uses a fixed size character buffer
//!as its formatting buffer.
template <class CharT, class CharTraits = std::char_traits<CharT> >
class basic_bufferstream
   : public std::basic_iostream<CharT, CharTraits>
{
   public:                         // Typedefs
   typedef typename std::basic_ios
      <CharT, CharTraits>::char_type          char_type;
   typedef typename std::basic_ios<char_type, CharTraits>::int_type     int_type;
   typedef typename std::basic_ios<char_type, CharTraits>::pos_type     pos_type;
   typedef typename std::basic_ios<char_type, CharTraits>::off_type     off_type;
   typedef typename std::basic_ios<char_type, CharTraits>::traits_type  traits_type;
   //!Constructor. Does not throw.
   basic_bufferstream(std::ios_base::openmode mode
                     = std::ios_base::in | std::ios_base::out);
   //!Constructor. Assigns formatting buffer. Does not throw.
   basic_bufferstream(CharT *buffer, std::size_t length,
                     std::ios_base::openmode mode
                        = std::ios_base::in | std::ios_base::out);
   //!Returns the address of the stored stream buffer.
   basic_bufferbuf<CharT, CharTraits>* rdbuf() const;
   //!Returns the pointer and size of the internal buffer.
   //!Does not throw.
   std::pair<CharT *, std::size_t> buffer() const;
   //!Sets the underlying buffer to a new value. Resets
   //!stream position. Does not throw.
   void buffer(CharT *buffer, std::size_t length);
};
//Some typedefs to simplify usage
typedef basic_bufferstream<char>     bufferstream;
typedef basic_bufferstream<wchar_t>  wbufferstream;
// ...

При чтении из буфера фиксированного размера bufferstream активирует флаг эндбита, если мы попытаемся прочитать адрес за пределами конца буфера. При записи в буфер фиксированного размера bufferstream активирует флаг бадбита, если произойдет переполнение буфера и запрещает запись. Таким образом, форматирование буфера фиксированного размера через bufferstream является безопасным и эффективным и предлагает хорошую альтернативу функциям sprintf/sscanf. Давайте посмотрим пример:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/streams/bufferstream.hpp>
#include <vector>
#include <iterator>
#include <cstddef>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Fill data
   std::vector<int> data;
   data.reserve(100);
   for(int i = 0; i < 100; ++i){
      data.push_back(i);
   }
   const std::size_t BufferSize = 100*5;
   //Allocate a buffer in shared memory to write data
   char *my_cstring =
      segment.construct<char>("MyCString")[BufferSize](0);
   bufferstream mybufstream(my_cstring, BufferSize);
   //Now write data to the buffer
   for(int i = 0; i < 100; ++i){
      mybufstream << data[i] << std::endl;
   }
   //Check there was no overflow attempt
   assert(mybufstream.good());
   //Extract all values from the shared memory string
   //directly to a vector.
   std::vector<int> data2;
   std::istream_iterator<int> it(mybufstream), itend;
   std::copy(it, itend, std::back_inserter(data2));
   //This extraction should have ended will fail error since
   //the numbers formatted in the buffer end before the end
   //of the buffer. (Otherwise it would trigger eofbit)
   assert(mybufstream.fail());
   //Compare data
   assert(std::equal(data.begin(), data.end(), data2.begin()));
   //Clear errors and rewind
   mybufstream.clear();
   mybufstream.seekp(0, std::ios::beg);
   //Now write again the data trying to do a buffer overflow
   for(int i = 0, m = data.size()*5; i < m; ++i){
      mybufstream << data[i%5] << std::endl;
   }
   //Now make sure badbit is active
   //which means overflow attempt.
   assert(!mybufstream.good());
   assert(mybufstream.bad());
   segment.destroy_ptr(my_cstring);
   return 0;
}

Как видно, bufferstream предлагает эффективный способ форматирования данных без какого-либо выделения и дополнительных копий. Это очень полезно во встраиваемых системах или при форматировании внутри критически важных для времени циклов, где дополнительные копии струнного потока будут слишком дорогими. В отличие от sprintf/sscanf, он имеет защиту от переполнения буфера. Как мы знаем, согласно техническому отчету по производительности C++, можно разработать эффективные iostream для встроенных платформ, поэтому этот класс буферного потока удобен для форматирования данных в стековые, статические или общие буферы памяти.


PrevUpHomeNext

Статья Direct iostream formatting: vectorstream and bufferstream раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 16. Boost.Interprocess может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 16. Boost.Interprocess ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 12:37:06/0.028984069824219/1