Boost interprocess: cout a string variable when iterating through a map that references an object from a struct

后端 未结 3 936
孤街浪徒
孤街浪徒 2021-01-18 19:23

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

相关标签:
3条回答
  • 2021-01-18 19:37

    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.

    0 讨论(0)
  • 2021-01-18 19:38

    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.

    0 讨论(0)
  • 2021-01-18 19:43

    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>
    
    0 讨论(0)
提交回复
热议问题