making non-shared copies of boost::interprocess shared memory objects

前端 未结 1 1207
礼貌的吻别
礼貌的吻别 2020-12-10 23:10

I have implemented various classes that are designed to be used in boost::interprocess shared memory segments. All their constructors employ allocator<

相关标签:
1条回答
  • 2020-12-10 23:49

    Ok, you've run into the Frequently Annoying Edgecase that template-template arguments aren't first class citizens in C++ (you cannot pass them around/typedef them):

    • How to transmit a template?

    What shall we do?

    1. allocator::rebind<T>

      Allocators have a rebind mechanism, I daresay precisely because of this. So you can pass a alloc<void> as if it is the open template, because you can always get from there to a sibling allocator type by doing Alloc::rebind<T>::other.

    2. Add to this the fact that allocators usually have conversion constructors that do this rebinding, you don't need to be overly specific in many places taking allocators

    3. in c++11, scoped_allocators have been introduced to avoid having to manually pass allocator instances in a number of places that will do internal construction of elements (e.g. emplace_back).

      There's library magic in place that will automatically add the allocator instance from the container's scoped_allocator as the last constructor argument (by default). Boost Container library has backported the scoped_allocator_adaptor concept to c++03 so you can use it.

    Here's a full sample that shows you how to solve the issues you had, and also, how you can mix the heap-based Bar instances with the shared-memory Foo instance:

    foo2.add(bar1); // this works because of ... MAGIC!
    

    Which works due to the scoped_allocator mentioned above.

    Live On Coliru

    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/allocators/allocator.hpp>
    #include <boost/interprocess/containers/vector.hpp>
    #include <boost/container/scoped_allocator.hpp>
    
    namespace bip = boost::interprocess;
    
    namespace generic { 
    
        template <typename T, typename Alloc/* = std::allocator<T>*/ >
            using vector = bip::vector<T, typename Alloc::template rebind<T>::other >;
    
        template <typename Alloc> struct Bar {
            typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator
    
            // only require allocator if not default-constructible
            Bar(Alloc alloc = Alloc()) : mInts(alloc) {}
    
            // conversion constructor so we can convert between allocators 
            template <typename OtherAlloc>
                Bar(Bar<OtherAlloc> const& rhs, Alloc alloc = Alloc())
                    : mInts(rhs.mInts.begin(), rhs.mInts.end(), alloc) 
                {
                }
    
            void Report() const;
    
            void add(int i) { mInts.emplace_back(i); }
    
          private:
            template<typename OtherAlloc> friend struct Bar; // we can see each other's mInts
            typedef vector<int, Alloc> ints_t;
            ints_t mInts;
        };
    
        template <typename Alloc> struct Foo {
            typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator
    
            Foo(Alloc alloc = Alloc()) : mBars(alloc) {}
            void Report() const;
    
            template <typename Bar>
            void add(Bar const& bar) { mBars.emplace_back(bar); }
    
          private:
            typedef vector<Bar<Alloc>, Alloc> mbars_t;
            mbars_t mBars;
        };
    }
    
    namespace heap {
        using VAlloc = std::allocator<void>;
    
        using Bar = generic::Bar<VAlloc>;
        using Foo = generic::Foo<VAlloc>;
    }
    
    namespace shared {
        using VAlloc = boost::container::scoped_allocator_adaptor<bip::allocator<void, bip::managed_shared_memory::segment_manager> >;
    
        using Bar = generic::Bar<VAlloc>;
        using Foo = generic::Foo<VAlloc>;
    }
    
    template <typename Alloc> void generic::Bar<Alloc>::Report() const {
        std::cout << "[";
        for (typename ints_t::const_iterator it = mInts.begin(); it != mInts.end(); it++)
            std::cout << (it == mInts.begin() ? "" : ", ") << *it;
        std::cout << "]\n";
    }
    
    template <typename Alloc>
    void generic::Foo<Alloc>::Report() const {
        for (typename mbars_t::const_iterator it = mBars.begin(); it != mBars.end(); it++)
            it->Report();
        std::cout << "\n";
    }
    
    int main(void) {
        struct shm_remove {
            shm_remove()  { bip::shared_memory_object::remove("MySharedMemory"); }
            ~shm_remove() { bip::shared_memory_object::remove("MySharedMemory"); }
        } remover;
    
        ///////////////////////////////////
        // heap based:
        std::cout << "Heap based storage: \n";
    
        heap::Foo foo1;
        heap::Bar bar1;
    
        bar1.add(42);
        bar1.add(2);
        bar1.add(-99);
    
        foo1.add(bar1);
        foo1.Report();
    
        /////////////////////////////////
        std::cout << "Shared memory storage: \n";
        bip::managed_shared_memory seg(bip::create_only, "MySharedMemory", 65536);
        shared::VAlloc shalloc(seg.get_segment_manager());
    
        shared::Foo foo2(shalloc);
        shared::Bar bar2(shalloc);
    
        bar2.add(43);
        bar2.add(3);
        bar2.add(-98);
    
        foo2.add(bar2); // of course this works
        foo2.add(bar1); // this works because of ... MAGIC!
        foo2.Report();
    }
    

    Prints:

    Heap based storage: 
    [42, 2, -99]
    
    Shared memory storage: 
    [43, 3, -98]
    [42, 2, -99]
    
    0 讨论(0)
提交回复
热议问题