Общая память, файлы с картой памяти и все механизмы Boost.Interprocess ориентированы на эффективность. Причина, по которой используется общая память, заключается в том, что это самый быстрый доступный механизм IPC. При передаче текстовых сообщений через общую память необходимо форматировать сообщение. Очевидно, что C++ предлагает iostream-фреймворк для этой работы.
Некоторые программисты ценят безопасность и дизайн iostream для форматирования памяти, но считают, что семейство струнных потоков далеко не эффективно не при форматировании, а при получении отформатированных данных в строку или при настройке строки, из которой поток будет извлекать данные. Пример:
std::string my_text = "...";
int number;
std::istringstream input_processor;
input_processor.str(my_text);
while(/*...*/){
input_processor >> number;
}
std::ostringstream output_processor;
while(/*...*/){
output_processor << number;
}
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:
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;
basic_vectorstream(std::ios_base::openmode mode
= std::ios_base::in | std::ios_base::out);
template<class Parameter>
basic_vectorstream(const Parameter ¶m, std::ios_base::openmode mode
= std::ios_base::in | std::ios_base::out);
~basic_vectorstream(){}
basic_vectorbuf<CharVector, CharTraits>* rdbuf() const;
void swap_vector(vector_type &vect);
const vector_type &vector() const;
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 ()
{
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",
65536);
MyVector *myvector =
segment.construct<MyVector>("MyVector")
(IntAllocator(segment.get_segment_manager()));
myvector->reserve(100);
for(int i = 0; i < 100; ++i){
myvector->push_back(i);
}
MyVectorStream myvectorstream(CharAllocator(segment.get_segment_manager()));
myvectorstream.reserve(100*5);
for(std::size_t i = 0, max = myvector->size(); i < max; ++i){
myvectorstream << (*myvector)[i] << std::endl;
}
MyVector *myvector2 =
segment.construct<MyVector>("MyVector2")
(IntAllocator(segment.get_segment_manager()));
myvector2->reserve(100);
std::istream_iterator<int> it(myvectorstream), itend;
std::copy(it, itend, std::back_inserter(*myvector2));
assert(std::equal(myvector->begin(), myvector->end(), myvector2->begin()));
MyString stringcopy (myvectorstream.vector());
MyString *mystring =
segment.construct<MyString>("MyString")
(CharAllocator(segment.get_segment_manager()));
myvectorstream.swap_vector(*mystring);
assert(stringcopy == *mystring);
segment.destroy_ptr(myvector2);
segment.destroy_ptr(myvector);
segment.destroy_ptr(mystring);
return 0;
}
Как видно, векторный поток предлагает простой и безопасный способ эффективного форматирования iostream, но во многих случаях нам приходится считывать или записывать отформатированные данные из / в буфер символов фиксированного размера (статический буфер, c-струна или любая другая). Из-за накладных расходов на стрингстрим многие разработчики (особенно во встроенных системах) выбирают семейство sprintf. Классы bufferstream предлагают интерфейс iostream с прямым форматированием в буфере памяти фиксированного размера с защитой от переполнения буфера. Это и есть интерфейс:
template <class CharT, class CharTraits = std::char_traits<CharT> >
class basic_bufferstream
: public std::basic_iostream<CharT, CharTraits>
{
public:
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;
basic_bufferstream(std::ios_base::openmode mode
= std::ios_base::in | std::ios_base::out);
basic_bufferstream(CharT *buffer, std::size_t length,
std::ios_base::openmode mode
= std::ios_base::in | std::ios_base::out);
basic_bufferbuf<CharT, CharTraits>* rdbuf() const;
std::pair<CharT *, std::size_t> buffer() const;
void buffer(CharT *buffer, std::size_t length);
};
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 ()
{
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",
65536);
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;
char *my_cstring =
segment.construct<char>("MyCString")[BufferSize](0);
bufferstream mybufstream(my_cstring, BufferSize);
for(int i = 0; i < 100; ++i){
mybufstream << data[i] << std::endl;
}
assert(mybufstream.good());
std::vector<int> data2;
std::istream_iterator<int> it(mybufstream), itend;
std::copy(it, itend, std::back_inserter(data2));
assert(mybufstream.fail());
assert(std::equal(data.begin(), data.end(), data2.begin()));
mybufstream.clear();
mybufstream.seekp(0, std::ios::beg);
for(int i = 0, m = data.size()*5; i < m; ++i){
mybufstream << data[i%5] << std::endl;
}
assert(!mybufstream.good());
assert(mybufstream.bad());
segment.destroy_ptr(my_cstring);
return 0;
}
Как видно, bufferstream предлагает эффективный способ форматирования данных без какого-либо выделения и дополнительных копий. Это очень полезно во встраиваемых системах или при форматировании внутри критически важных для времени циклов, где дополнительные копии струнного потока будут слишком дорогими. В отличие от sprintf/sscanf, он имеет защиту от переполнения буфера. Как мы знаем, согласно техническому отчету по производительности C++, можно разработать эффективные iostream для встроенных платформ, поэтому этот класс буферного потока удобен для форматирования данных в стековые, статические или общие буферы памяти.