Using STL containers for boost::interprocess::mana

2019-02-28 20:57发布

问题:

Consider the following situation:

class Helper
{
    public:
        // Getters and setters are present!
    private:
        int i;
        std::map<int, boost::interprocess::managed_shared_memory> shm;
}

int main()
{
    boost::interprocess::managed_shared_memory shmInfo(boost::interprocess::open_or_create, "Test", 1024);

    boost::interprocess::map<int, Helper> myMap = shmInfo.construct< boost::interprocess::map<int, Helper> >("Memory");
}

myMap (which is a map of int and Helper) is constructed on the shared_memory. In turn, I want Helper to hold a map of int and boost::interprocess::managed_shared_memory.

When I am trying to use std::map in Helper, I am getting compiler errors:

error C2248: 'boost::interprocess::basic_managed_shared_memory::basic_managed_shared_memory' : cannot access private member declared in class 'boost::interprocess::basic_managed_shared_memory'

How can I achieve this?

回答1:

Okay, here's a version that works Live On Coliru

Let me address some of the issues your code had:


  1. Let's start at the top

    class Helper
    {
        public:
            // Getters and setters are present!
        private:
            int i;
            std::map<
    -----------^
    
  2. Using std::map is oft troublesome with shared memory allocators because it does allocations right from within the constructor. boost::container::map and boost::interprocess::map donot, so you should prefer them when using Boost Interprocess allocators. Using a template alias, you can cut down on the complexity of declaring such a shared map:

    template <typename T> using shm_alloc = bip::allocator<T, bip::managed_shared_memory::segment_manager>;
    template <typename K, typename V> using shared_map = bip::map<K, V, std::less<K>, shm_alloc<std::pair<K const, V> > >;
    

    Now you can "just" say shared_map<K,V> where you would have previously said std::map<K,V>.

                      int, boost::interprocess::managed_shared_memory> shm;
    --------------------------------------------^
    
  3. Two points here:

    1. boost::interprocess::managed_shared_memory is not copyable (because it owns a shared memory resource). In my example I used shared_ptr<managed_shared_memory> to work around this. You could probably use raw pointers if you are sure the lifetime of the objects is going to be longer than that of the map that contains the pointer.

    2. it makes little sense to have standard-allocated containers in a Helper class that lives in shared memory (std::map will simply point to objects on the process-local heap and that leads to UB when referenced from another process). So you should specify a boost interprocess allocator to put the container elements into shared memory. For technical reasons, this implies you have to specify the key comparator (event though it's just the default, std::less<K>).

    }
    -^
    
  4. Missing ; :)

    int main()
    {
        boost::interprocess::managed_shared_memory shmInfo(boost::interprocess::open_or_create, "Test", 1024);
    
        boost::interprocess::map
    -------------------------^ 
    
  5. Even though you now use boost::interprocess::map, you still didn't indicate the allocator type. Again (as above), you can use the shared_map alias.

                                <int, Helper> 
    
    -----------------------------------------^ 
    
  6. Missing * for pointer result type.

                                              myMap = shmInfo.construct< boost::interprocess::map<int, Helper> >("Memory");
    
    ----------------------------------------------------------------------------------------------------------------------^ 
    
  7. You forgot to invoke the constructor proxy object.

    }
    
  8. Loose remarks:

    • you need a constructor in Helper so you can pass the proper allocator instance to the constructor of the shm field
    • using typedefs (or in my sample, template aliases) really makes your code a lot more maintainable.
    • note that we have two allocator instances here, the outer_alloc (for the Helpers map) and the inner_alloc (for Helper::shm)

Full Working Sample

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

namespace bip = boost::interprocess;

using ShMemRef = boost::shared_ptr<bip::managed_shared_memory>;
template <typename T> using shm_alloc = bip::allocator<T, bip::managed_shared_memory::segment_manager>;
template <typename K, typename V> using shared_map = bip::map<K, V, std::less<K>, shm_alloc<std::pair<K const, V> > >;

class Helper
{
  public:
    using inner_alloc = shm_alloc<std::pair<int, ShMemRef>>;
    Helper(inner_alloc const& instance) : shm(instance) {}
  private:
    int i;
    shared_map<int, ShMemRef> shm;
};

int main()
{
    ShMemRef shmInfo = boost::make_shared<bip::managed_shared_memory>(bip::open_or_create, "Main", 1024);

    using outer_alloc = shm_alloc<std::pair<const int, Helper>>;
    outer_alloc oa_instance(shmInfo->get_segment_manager());

    shared_map<int, Helper>* myHelpers = shmInfo->construct<shared_map<int, Helper>>("Memory")(oa_instance);

    Helper::inner_alloc ia_instance(shmInfo->get_segment_manager());
    Helper helper1(ia_instance), helper2(ia_instance), helper3(ia_instance);
    myHelpers->emplace(1, helper1);
    myHelpers->emplace(2, helper2);
    myHelpers->emplace(3, helper3);
}