want to efficiently overcome mismatch between key types in a map in Boost.Interprocess shared memory

前端 未结 2 2028
我在风中等你
我在风中等你 2020-12-02 01:23

I\'m creating a map (from string to string in this example) in shared memory using Boost.Interprocess. The compiler seems to want to force me, during retrieval from the map,

2条回答
  •  天涯浪人
    2020-12-02 01:54

    You can use a custom comparator

       struct MyLess {
            template 
                bool operator()(const T&t, const U&u) const
            {
                return t

    In your code you can just typedef it as StringComparator

    UPDATE To the comments


    Multi Index To The Rescue

    If you want to replace the std::map/boost::container::map with a Boost Multi Index container (which supports lookup by CompatibleKey), here's a demo of how to do it:

    I've borrowed some of the idea's from the documentation section Emulating standard containers with multi_index_container.

    Note that std::string as the lookup key still won't work, but you can easily use .c_strio() in that event.

    Live On Coliru

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    namespace emulation {
        template 
            struct mutable_pair
            {
                typedef T1 first_type;
                typedef T2 second_type;
    
                mutable_pair(Alloc alloc):first(T1(alloc)),second(T2(alloc)){}
                mutable_pair(const T1& f,const T2& s):first(f),second(s){}
                mutable_pair(const std::pair& p):first(p.first),second(p.second){}
    
                T1         first;
                mutable T2 second;
            };
    
        using namespace boost::multi_index;
    
        template  >
            using map = multi_index_container<
                Element,
                indexed_by<
                    ordered_unique,Compare>
                >,
                typename Allocator::template rebind::other
            >;
    
      template  >
        using multimap = multi_index_container<
            Element,
            indexed_by<
                ordered_non_unique,Compare>
            >,
            typename Allocator::template rebind::other
        >;
    
      template  
          struct wrap_map : map {
              typedef map base_type;
              typedef typename base_type::template nth_index<0>::type index_type;
    
              wrap_map(Allocator alloc) : base_type({}, alloc)
              {
              }
    
              wrap_map(Compare cmp, Allocator alloc) : base_type(
                      typename base_type::ctor_args_list{
                        typename index_type::ctor_args { typename index_type::key_from_value {}, cmp }
                      },
                      alloc)
              {
              }
          };
    }
    
    // Typedefs of allocators and containers
    namespace Shared {
        typedef boost::interprocess::managed_shared_memory Segment;
        typedef boost::interprocess::managed_shared_memory::segment_manager SegmentManager;
        typedef boost::interprocess::allocator Allocator;
        typedef boost::interprocess::allocator CharAllocator;
        typedef boost::interprocess::basic_string, CharAllocator> String;
    
        struct MyLess {
            template  bool operator()(const T &t, const U &u) const { return t < u; }
        };
        typedef MyLess StringComparator;
    
    
        typedef boost::interprocess::allocator StringAlloc;
        typedef emulation::mutable_pair MapItem;
        typedef boost::interprocess::allocator MapItemAllocator;
        typedef emulation::wrap_map Map;
    }
    
    int main(void) {
        struct shm_remove {
            shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
            ~shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
        } remover;
    
        // Create shared memory
        Shared::Segment seg(boost::interprocess::create_only, "MySharedMemory", 65536);
        Shared::Allocator alloc(seg.get_segment_manager());
    
        // An instance of the string comparator, to construct the map
        Shared::StringComparator cmp;
    
        // Construct the shared memory map
        Shared::Map *myMapPtr = seg.construct("myMap")(cmp, alloc);
    
        myMapPtr->emplace(Shared::String("foo", alloc), Shared::String("bar", alloc));
        myMapPtr->emplace(Shared::String("goo", alloc), Shared::String("car", alloc));
        myMapPtr->emplace(Shared::String("hoo", alloc), Shared::String("dar", alloc));
    
        Shared::String key("foo", alloc);
    
        // This is the point of the exercise:
        auto it = myMapPtr->find(key);
    
        if (it!=myMapPtr->end())
            std::cout << "Found: '" << it->first << "' -> '" << it->second << "'\n";
    
        // this is now okay too
        char szkey[] = "foo";
        it = myMapPtr->find(szkey);
        if (it!=myMapPtr->end())
            std::cout << "Found: '" << it->first << "' -> '" << it->second << "'\n";
    
        // this is now okay too
        std::string skey("foo");
        it = myMapPtr->find(skey.c_str());
        if (it!=myMapPtr->end())
            std::cout << "Found: '" << it->first << "' -> '" << it->second << "'\n";
    
        return 0;
    }
    

    Prints:

    Found: 'foo' -> 'bar'
    Found: 'foo' -> 'bar'
    Found: 'foo' -> 'bar'
    

    Scoped Allocators For Extra Awesomesauce?

    Now, interestingly, Boost Container supports Scoped Allocators, so you could do away with the repeated passing of the allocators, however, Boost Multi Index sadly doesn't support it fully. Here's a halfway approach that's about as far as I could get it (still somewhat user friendlier):

    Live On Coliru

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    
    #include 
    #include 
    #include 
    
    namespace emulation {
        template 
            struct mutable_pair
            {
                typedef Alloc allocator_type;
                typedef T1 first_type;
                typedef T2 second_type;
    
                mutable_pair(Alloc alloc):first(T1(alloc)),second(T2(alloc)){}
                mutable_pair(const T1& f,const T2& s):first(f),second(s){}
                mutable_pair(const std::pair& p):first(p.first),second(p.second){}
    
                template 
                mutable_pair(const U& f,const V& s, Alloc2 alloc):first(f, alloc),second(s, alloc){}
    
                T1         first;
                mutable T2 second;
            };
    
        using namespace boost::multi_index;
    
        template  >
            using map = multi_index_container<
                Element,
                indexed_by<
                    ordered_unique,Compare>
                >,
                typename Allocator::template rebind::other
            >;
    
      template  >
        using multimap = multi_index_container<
            Element,
            indexed_by<
                ordered_non_unique,Compare>
            >,
            typename Allocator::template rebind::other
        >;
    
      template  
          struct wrap_map : map {
              typedef map base_type;
              typedef typename base_type::template nth_index<0>::type index_type;
    
              wrap_map(Allocator alloc) : base_type({}, alloc)
              { 
              }
    
              wrap_map(Compare cmp, Allocator alloc) : base_type(
                      typename base_type::ctor_args_list{
                        typename index_type::ctor_args { typename index_type::key_from_value {}, cmp }
                      },
                      alloc)
              { 
              }
          };
    }
    
    
    // Typedefs of allocators and containers
    namespace Shared {
        typedef boost::interprocess::managed_shared_memory Segment;
        typedef Segment::segment_manager SegmentManager;
        typedef boost::container::scoped_allocator_adaptor > Allocator;
    
        typedef Allocator::rebind::other CharAllocator;
        typedef boost::interprocess::basic_string, CharAllocator> String;
    
        struct MyLess {
            template  bool operator()(const T &t, const U &u) const { return t < u; }
        };
        typedef MyLess StringComparator;
    
        typedef emulation::mutable_pair MapItem;
        typedef Allocator::rebind::other MapItemAllocator;
        typedef emulation::wrap_map Map;
    }
    
    int main(void) {
        struct shm_remove {
            shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
            ~shm_remove() { boost::interprocess::shared_memory_object::remove("MySharedMemory"); }
        } remover;
    
        // Create shared memory
        Shared::Segment seg(boost::interprocess::create_only, "MySharedMemory", 65536);
        Shared::Allocator alloc(seg.get_segment_manager());
    
        // An instance of the string comparator, to construct the map
        Shared::StringComparator cmp;
    
        // Construct the shared memory map
        Shared::Map *myMapPtr = seg.construct("myMap")(cmp, alloc);
    
        myMapPtr->emplace("foo", "bar", alloc);
        myMapPtr->emplace("goo", "car", alloc);
        myMapPtr->emplace("hoo", "dar", alloc);
    
        // This the only version I can get to work.  But it forces you to create a
        // copy of the key you are searching for, in the managed segment.
        Shared::String key("foo", alloc);     
    
        // This is the point of the exercise:
        auto it = myMapPtr->find(key);
    
        if (it!=myMapPtr->end())
            std::cout << "Found: '" << it->first << "' -> '" << it->second << "'\n";
    
        // this is now okay too
        char szkey[] = "foo";
        it = myMapPtr->find(szkey);
        if (it!=myMapPtr->end())
            std::cout << "Found: '" << it->first << "' -> '" << it->second << "'\n";
    
        // this is now okay too
        std::string skey("foo");
        it = myMapPtr->find(skey.c_str());
        if (it!=myMapPtr->end())
            std::cout << "Found: '" << it->first << "' -> '" << it->second << "'\n";
    
        return 0;
    }
    

    Also printing

    Found: 'foo' -> 'bar'
    Found: 'foo' -> 'bar'
    Found: 'foo' -> 'bar'
    

提交回复
热议问题