4

given these types (from here, more or less):

typedef boost::interprocess::managed_shared_memory::segment_manager SegmentManager;
typedef boost::interprocess::allocator<char, SegmentManager> CharAllocator;

typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> ShmString;

typedef boost::interprocess::allocator<ShmString, SegmentManager> StringAllocator;

typedef boost::interprocess::vector<ShmString, StringAllocator> ShmStringVector;

Whenever I retrieve a ShmString from the ShmStringVector, the free size of my shared memory segment goes down.

What I think is happening is whenever ShmString is constructed, even as a stack variable, it allocates space inside the shared memory segment.

When that ShmString goes out of scope, the memory gets returned. But if I'm close to capacity on my segment, I don't want accesses risking causing an overrun on the shared memory segment.

How can I get the string out without triggering another allocation?

In the example below, I tried

//shared mem segment previously created with 1,000,000 bytes allocated
managed_shared_memory segment(open_only, "somename");
CharAllocator charAllocator(segment.get_segment_manager());
ShmStringVector *vec = segment.find<ShmStringVector>("somevectorname").first;

vec->push_back(" ... big string ..."  );       // I can push a const char*
size_t sizeAfterInsertion = segment.get_free_memory();

ShmString   res1 = vec->at(0);  //works, but steals memory from shared memory, at least temporarily !
const char* res2 = vec->at(0);  //compiler error
std::string res3 = vec->at(0);  //compiler error 
size_t sizeAfterGet = segment2.get_free_memory();
size_t diff = sizeAfterInsertion - sizeAfterGet;

I proved to myself memory was being taken by declaring ShmString...

std::string size 170,     
before ins:    999232, after ins: 998976, shared memory taken by ins: 256 
                       after get: 998768, shared memory taken by get: 208
marathon
  • 7,881
  • 17
  • 74
  • 137

2 Answers2

4

As Wug mentions, you can use a reference.

Converting/assigning between string types:

If you really want a copy, outside the shared memory, copy to a regular (or other-allocator) string:

std::string copied(vec2->at(0).begin(), vec2->at(0).end());

Of course, the same works vice versa:

vec2[0].assign(copied.begin(), copied.end());
// Or
vec2->emplace_back({copied.begin(), copied.end()});

etc.

Using streams directly on shared memory

Note also that there is streaming support for 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>               MyStringStream;

Now you can use MyVectorStream to read/write to the shared memory string. It's akin to std::stringstream but without having to copy the data into the stream first.

E.g write some data to a vector:

   //Create the stream. To create the internal shared memory
   //basic_string we need to pass the shared memory allocator as
   //a constructor argument
   MyStringStream mystringstream(CharAllocator(segment.get_segment_manager()));

   //Reserve the internal string
   mystringstream.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){
      mystringstream << (*myvector)[i] << std::endl;
   }

And read it back to copy it directly into another shared memory string:

  //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(mystringstream), itend;
   std::copy(it, itend, std::back_inserter(*myvector2));

   //Compare vectors
   assert(std::equal(myvector->begin(), myvector->end(), myvector2->begin()));

This taken from the documentation sample and you can see it [Live On Coliru](http://coliru.stacked-crooked.com/a/7f1f450ac11edd4a

sehe
  • 374,641
  • 47
  • 450
  • 633
  • thanks for the detailed answer. When pushing things into the vector, what do you suggest? I also notice simply declaring a ShmString on the stack takes shared memory, the vec->push_back(str); takes even more by tripping the copy-constructor. Would boost::move be appropriate? – marathon Oct 12 '14 at 13:46
  • 1
    You can use emplace (demo: **[Live On Coliru](http://coliru.stacked-crooked.com/a/780489991a345ca6)**), see also e.g. [this recent answer](http://stackoverflow.com/questions/26280041/how-to-i-create-a-boost-interprocess-vector-of-interprocess-containers/26281947#26281947). Of course, this could have been posted as a separate question – sehe Oct 12 '14 at 19:32
2

Use a reference.

ShmString &res1 = vec2->at(0);

Remember that this isn't actually making a copy of the string, but referring to the original string. This will be perfectly sufficient for just reading its contents (in which you should declare res1 to be const as well).

Wug
  • 12,956
  • 4
  • 34
  • 54