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

后端 未结 3 957
孤街浪徒
孤街浪徒 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: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 
    
    #include 
    #include 
    
    #include 
    #include 
    
    #include 
    #include 
    
    namespace bip = boost::interprocess;
    
    namespace Shared {
        using Segment = bip::managed_shared_memory;
        using Manager = Segment::segment_manager;
    
        template  using Alloc 
            = boost::container::scoped_allocator_adaptor >;
    
        using String = bip::basic_string, Alloc >;
    
        template  > using Map 
            = bip::map > >;
    
        struct Order {
            using allocator_type = Alloc;
    
            template 
            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>("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: the key seems to duplicate the integer value inside Order.

    Option 1: Map

    You could either make it Map 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>("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 >
        >,
        Alloc
    >;
    

    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.

提交回复
热议问题