I\'m using boost::interprocess to share objects between processes. I have two files, a \"server.cpp\" that generates a struct object and passes the object into a map with an
Some great info from previous posters led me to a simple answer of my own. Use a char array in the struct, then read it into a string as follows:
std::cout<<iter->first<<" "<<iter->second.ORDERTYPE<<" "<<string(iter->second.MYID)<<"\n";
here is the struct I speak of:
struct order {
char MYID[100];
int ORDERTYPE;
char DATE_UPDATED[64];
};
order o
here is how i passed the struct into memory:
m_pmap->insert(std::pair<const int, order>(i, (order)o));
now i am able to write a map of structs containing various types including strings, to memory, and retrieve them from memory.
Like Justin mentioned, std::string
is itself a container that dynamically allocates.
Just using Interprocess's string
is not enough. In fact, that's just boost::container::basic_string<>
really.
The important thing is using the allocator.
However, using a map with an allocator, and passing the allocator whenever you need to construct the contained containers (ad nauseam) is annoying.
These make it so that you don't have to know the allocator, any container that knows how to use scoped allocators will pass the allocator onto the nested container.
Live On Coliru ¹
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <iostream>
#include <string>
namespace bip = boost::interprocess;
namespace Shared {
using Segment = bip::managed_shared_memory;
using Manager = Segment::segment_manager;
template <typename T> using Alloc
= boost::container::scoped_allocator_adaptor<bip::allocator<T, Manager> >;
using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;
template <typename K, typename V, typename Cmp = std::less<K> > using Map
= bip::map<K, V, Cmp, Alloc<std::pair<K const, V> > >;
struct Order {
using allocator_type = Alloc<char>;
template <typename S, typename Alloc>
Order(int i, S const& s, Alloc alloc = {}) : i(i), s(s, alloc) {}
int i;
String s;
};
}
int main() {
try {
using namespace Shared;
Segment segment(bip::open_or_create, "095540a3-ceaa-4431-828d-df21d5e384ae", 65536);
auto& pmap = *segment.find_or_construct<Map<int, Order>>("MySHMMapName")(segment.get_segment_manager());
if (pmap.empty()) {
std::cout << "Inserting data\n";
auto insert = [&pmap](int i, auto&& s) {
using namespace std;
pmap.emplace(piecewise_construct, tie(i), tie(i, s));
};
insert(1, "one");
insert(2, "two");
insert(3, "three");
} else {
std::cout << "Existing data:\n";
for (auto& [k,v] : pmap) {
std::cout << k << " " << v.i << " " << v.s << "\n";
}
}
} catch (std::exception &e) {
std::cout << " error " << e.what() << std::endl;
bip::shared_memory_object::remove("095540a3-ceaa-4431-828d-df21d5e384ae");
}
}
I notice that the map is Map<int, Order>
: the key seems to duplicate the integer value inside Order
.
Map<int, String>
You could either make it Map<int, String>
and get a much more streamlined experience (because there's no need for std::piecewise_construct
):
Live On Coliru
auto& pmap = *segment.find_or_construct<Map<int, String>>("MySHMMapName")(segment.get_segment_manager());
if (pmap.empty()) {
std::cout << "Inserting data\n";
pmap.emplace(1, "one");
pmap.emplace(2, "two");
pmap.emplace(3, "three");
}
Alternatively, you should consider using Multi-Index which is able to index Order
directly by a member of the type:
Live On Coliru
namespace bmi = boost::multi_index;
using Table = bmi::multi_index_container<Order,
bmi::indexed_by<
bmi::ordered_unique< bmi::member<Order, int, &Order::i> >
>,
Alloc<Order>
>;
Sadly, Multi Index doesn't quite play as well with nested types using allocators, so you'll have to pass it along again:
if (pmap.empty()) {
std::cout << "Inserting data\n";
pmap.emplace(1, "one", pmap.get_allocator());
pmap.emplace(2, "two", pmap.get_allocator());
pmap.emplace(3, "three", pmap.get_allocator());
} else {
std::cout << "Existing data:\n";
for (Order const& o : pmap) {
std::cout << o.i << " " << o.s << "\n";
}
// demonstrate lookup:
std::cout << "Finding element 2:" << pmap.find(2)->s << "\n";
}
Prints
Existing data:
1 one
2 two
3 three
Finding element 2:two
¹ using mapped files on Coliru instead. The refactored code makes this a 1-line change.
But for some reason the client executable throws a segmentation fault if I try to access a string rather than a float or an integer in the client.
It looks like you are using std::string
in your structure within the shared memory map. You cannot do this, since std::string
allocates it's own memory which goes out of scope with boost::interprocess
. You should try using the boost::interprocess::basic_string
type in place of std::string
. There is an example of how to use this in an interprocess map in the Boost documentation
#include <boost/interprocess/containers/string.hpp>