5

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 int index; and a "client.cpp" file that retrieves the memory data and iterates through the data, outputting to console.

The struct looks like this:

    struct mydata o {
    string MY_STRING;
    int MY_INT; 
};

And the object:

mydata o;
o.MY_STRING = "hello";
o.MY_INT = 45;

Both server and client compile correctly. 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. For example the below second.MY_INT will cout to console, but second.MY_STRING throws this error when running.

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <functional>
#include <utility>
#include <iostream>
#include <string>
#include "objects.cpp" //definitions for objects
using std::string;
using namespace boost::interprocess;

    int main ()
{

    try
    {
            managed_shared_memory segment(open_or_create, "SharedMemoryName",65536);

            //Again the map<Key, MappedType>'s value_type is std::pair<const Key, MappedType>, so the allocator must allocate that pair.
            typedef int    KeyType;
            typedef order  MappedType;
            typedef std::pair<const int, order> ValueType;

            //Assign allocator 
            typedef allocator<ValueType, managed_shared_memory::segment_manager> ShmemAllocator;

            //The map
            typedef map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> MySHMMap;

            //Initialize the shared memory STL-compatible allocator
            ShmemAllocator alloc_inst (segment.get_segment_manager());

            //access the map in SHM through the offset ptr                                                         
            MySHMMap :: iterator iter;
            offset_ptr<MySHMMap> m_pmap = segment.find<MySHMMap>("MySHMMapName").first;

            iter=m_pmap->begin();
            for(; iter!=m_pmap->end();iter++)
            {
                   //std::cout<<"\n "<<iter->first<<" "<<iter->second;
                   std::cout<<iter->first<<" "<<iter->second.MYINT<<" "<<iter->second.MYSTRING<<"\n";
            }
    }catch(std::exception &e)            
    {
            std::cout<<" error  " << e.what() <<std::endl;
           shared_memory_object::remove("SharedMemoryName");
    }
    return 0;
}

the error when running:

Segmentation fault (core dumped)

I'm pretty sure the server is passing the entire object to memory, and the client can retrieve it (as i can access some of the objects attributes), and that its just a formatting issue.

Pablo
  • 13,271
  • 4
  • 39
  • 59

3 Answers3

3

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.

Enter Scoped Allocators

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");
    }
}

Making It More Elegant

I notice that the map is Map<int, Order>: the key seems to duplicate the integer value inside Order.

Option 1: 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");
}

Option 2: Multi Index

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.

sehe
  • 374,641
  • 47
  • 450
  • 633
1

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>
Justin Randall
  • 2,243
  • 2
  • 16
  • 23
1

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.