Reputation: 21
I am following this Boost::Interprocess guide . I have structure which has std::string
as member and I have created multimap of that structure with std::string
as key values.
I can access std::string
from structure in producer code . But, when I try to access the same std::string
member in consumer code then I am getting core dump.
Can some one help me. Thanks in advance. I am giving piece of code below :
Producer code :
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
struct temp
{
int x;
int y;
std::string name;
};
int main ()
{
using namespace boost::interprocess;
try
{
shared_memory_object::remove("MySharedMultimap");
managed_shared_memory segment
(create_only
,"MySharedMultimap" //segment name
,65536); //segment size in bytes
typedef std::string KeyType;
typedef struct temp MappedType;
typedef std::pair<const std::string, struct temp> ValueType;
typedef allocator< ValueType, managed_shared_memory::segment_manager>
ShmemAllocator;
typedef multimap<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> MyMultimap;
//Initialize shared memory STL-compatible allocator
const ShmemAllocator alloc_inst (segment.get_segment_manager());
//Construct a shared memory
MyMultimap *mymultimap =
segment.construct<MyMultimap>("MyMultimap") //object name
(alloc_inst);//first ctor parameter
std::string s = "key1";
mymultimap->insert(std::pair<KeyType, MappedType>(s, t1));
}
catch(...)
{
shared_memory_object::remove("MySharedMultimap");
throw;
}
}
Consumer code :
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
struct temp
{
int x;
int y;
std::string name;
};
int main ()
{
using namespace boost::interprocess;
try
{
managed_shared_memory segment
(open_only
,"MySharedMultimap"); //segment name
typedef std::string KeyType;
typedef struct temp MappedType;
typedef std::pair<const std::string, struct temp> ValueType;
//Alias an STL compatible allocator of ints that allocates ints from the managed
//shared memory segment. This allocator will allow to place containers
//in managed shared memory segments
typedef allocator<ValueType , managed_shared_memory::segment_manager>
ShmemAllocator;
//Alias a vector that uses the previous STL-like allocator
typedef multimap< KeyType, MappedType, std::less<KeyType>, ShmemAllocator> MyMultimap;
//Find the vector using the c-string name
MyMultimap *mymultimap = segment.find<MyMultimap>("MyMultimap").first;
std:: cout << " multimap size size : " << mymultimap->size() << std::endl;
for(auto i = mymultimap->begin(); i != mymultimap->end(); ++i)
{
std::cout << i->first << std::endl; // core dump ( as we are accessing std::string )
std::cout << i->second.x << std::endl; // works fine
std::cout << i->second.name << std::endl; // core dump ( as we are accessing std::string )
}
}
catch(...){
shared_memory_object::remove("MySharedMultimap");
throw;
}
}
Upvotes: 2
Views: 467
Reputation: 392883
You need to use a shared-memory allocator. You already had the includes, why not use them?
First, I've simplified and combined your test programs into one: Live On Coliru. Note this still has the same problem.
You need to use a string with shared memory allocator:
namespace bip = boost::interprocess;
using Segment = bip::managed_shared_memory;
using Mgr = Segment::segment_manager;
template <typename T>
using Alloc = bip::allocator<T, Mgr>;
using String = bip::basic_string<char, std::char_traits<char>, Alloc<char>>;
Now, use that:
struct temp {
int x;
int y;
String name;
};
using MyMultimap = Multimap<String, temp>;
Which means you have to convert the "more data"
as well:
// assume sa = String::allocator_type sa(segment.get_segment_manager())
String more_data("more data", sa);
m.insert({String("key", sa), temp{1, 2, more_data}});
That works, Live On Coliru, printing:
multimap size size : 1
key
1
more data
That's a lot of manual allocator juggling. Use scoped allocators to reduce that:
template <typename T>
using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;
Now you can write:
m.emplace("key", temp{1, 2, String("more data", sa)});
To also propagate the allocator to the
temp::name
member, you need to maketemp
allocator-aware.multimap
is not a suited example to show this with because its interface doesn't really exercise it anyways.For completeness, here's a sketch of what it might look like:
struct temp { using String = Shared::String; using allocator_type = String::allocator_type; temp(temp const&) = default; temp(temp&&) = default; temp(temp const& rhs, allocator_type a) : x(rhs.x) , y(rhs.y) , name(rhs.name.data(), rhs.name.size(), a) {} temp(temp&& rhs, allocator_type a) : temp(std::move(rhs)) { if (a != name.get_allocator()) name = String(name.data(), name.size(), a); } template <typename Alloc> temp(int x, int y, char const* name, Alloc a = {}) : x(x) , y(y) , name(name, a) {} int x; int y; String name; };
One step ahead of your question: you will probably want to m.find("key")
, or .lower_bound
,.upper_bound
and .equal_range
. To avoid the same allocator jumble and inefficiently allocating temporaries like so:
String::allocator_type sa(m.get_segment_manager());
auto r = m.equal_range(String("key", sa));
Use a transparant comparison:
using MyMultimap = Multimap<String, temp, std::less<>>;
Now you can "just" write:
// key lookup:
for (auto& [k, v] : boost::make_iterator_range(m.equal_range("key"))) {
std::cout << k << "\n";
std::cout << v.x << "\n";
std::cout << v.name << "\n";
}
#include <boost/container/map.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/string.hpp>
#include <boost/container/vector.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
namespace bip = boost::interprocess;
namespace Shared {
namespace bc = boost::container;
#ifndef COLIRU
using Segment = bip::managed_shared_memory;
#else
using Segment = bip::managed_mapped_file;
#endif
using Mgr = Segment::segment_manager;
template <typename T>
using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;
template <typename K, typename V, typename Cmp = std::less<K>>
using Multimap = bc::multimap<K, V, Cmp, Alloc<std::pair<K const, V>>>;
using String = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
static auto Remove = bip::shared_memory_object::remove;
} // namespace Shared
struct temp {
using String = Shared::String;
using allocator_type = String::allocator_type;
temp(temp const&) = default;
temp(temp&&) = default;
temp(temp const& rhs, allocator_type a)
: x(rhs.x)
, y(rhs.y)
, name(rhs.name.data(), rhs.name.size(), a) {}
temp(temp&& rhs, allocator_type a) : temp(std::move(rhs)) {
if (a != name.get_allocator())
name = String(name.data(), name.size(), a);
}
template <typename Alloc>
temp(int x, int y, char const* name, Alloc a = {})
: x(x)
, y(y)
, name(name, a) {}
int x;
int y;
String name;
};
int main(int argc, char** /*unused*/) {
using namespace Shared;
using MyMultimap = Multimap<String, temp, std::less<>>;
auto constexpr shm_name = "MySharedMemory";
auto constexpr map_name = "MyMultimap";
try {
if (argc > 1) { // Producer
Remove(shm_name);
Segment segment(bip::create_only, shm_name, 65536);
auto* sm = segment.get_segment_manager();
auto& m = *segment.construct<MyMultimap>(map_name) // object name
(sm); // first ctor parameter
temp&& v{1, 2, "more data", sm};
m.emplace("key", v);
} else { // Consumer
Segment segment(bip::open_only, shm_name);
auto& m = *segment.find<MyMultimap>(map_name).first;
std::cout << " multimap size size : " << m.size() << "\n";
// key lookup:
for (auto& [k, v] : boost::make_iterator_range(m.equal_range("key"))) {
std::cout << k << "\n";
std::cout << v.x << "\n";
std::cout << v.name << "\n";
}
}
} catch (...) {
// remove(name);
throw;
}
}
Running with e.g.
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lrt -DCOLIRU
./a.out producer
./a.out
Prints
multimap size size : 1
key
1
more data
Upvotes: 2