制作boost :: interprocess共享内存对象的非共享副本

时间:2014-12-15 20:01:41

标签: c++ boost shared-memory

我已经实现了各种旨在用于boost::interprocess共享内存段的类。他们所有的构造函数都使用allocator<void,segment_manager>个引用 - 一些在我编写的定义中明确(如下面的Foo构造函数),有些只是因为它是boost库定义需要的,在boost库代码中我不应该改变(如下面的IndexVector)。

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>

typedef boost::interprocess::managed_shared_memory                   Segment;
typedef boost::interprocess::managed_shared_memory::segment_manager  SegmentManager;
typedef boost::interprocess::allocator< void, SegmentManager >       Allocator;

typedef size_t                                                       Index;
typedef boost::interprocess::allocator< Index, SegmentManager >      IndexAllocator;
typedef boost::interprocess::vector<    Index, IndexAllocator >      IndexVector;

class Foo
{
    public:
        Foo( const Allocator & alloc ) : mData( alloc ) {}
       ~Foo() {}

    private:
        IndexVector mData;

};

大多数情况下,这些对象位于共享内存中。但我有时想在 - 共享内存中创建它们的副本。我的问题是:我是否必须定义包含不同成员类型(Foo_Nonshared而不是我的共享std::vector<Index>类型)的完整不同类(例如IndexVector)并提供复制/转换功能他们?这将是很多工作和很多愚蠢的重复。我可以通过为现有的Foo类提供替代构造函数来减少重复,但是我不知道如何在没有分配器的情况下初始化IndexVector成员。

还是有一些不错的捷径?我想象一下我可以传递给allocator的某种特定Foo()实例,然后将其传递给IndexVector构造函数,这将被两者都识别为含义&#34;在非共享内存中分配&#34;。这样的事情存在吗?是否有一个&#34;虚拟部门经理&#34;管理香草非共享内存?或者还有其他解决这个问题的方法吗?

我希望能够获得与C ++ 03兼容的答案,尽管我也有兴趣学习C ++ 11 +的工作方式。

更新以下问题被标记为重复:我已阅读以前的类似问题:

并试图概括我在那里看到的东西,有一些成功和一些失败(见下面的清单)。有一些我无法解决的编译器错误,标记为ERROR-特别是我无法弄清楚如何实例化迭代这些高度&#34; meta&#34;的成员的方法。容器。但无论有没有这些错误,我还不知道如何将模板模板制作成可维护的解决方案(实际上,我的对象包含其他复杂对象的容器,其中包含更多容器,AFAICS使语法复杂化,超越了理智。 ..见标有&#34; hmm&#34;)的部分。

我想,最后,我可能需要重新设计,以避免在共享和堆内存中使用相同的对象。

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>

namespace bip = boost::interprocess; // warning: C++11 alias declaration

template <typename T, template<typename...> class Allocator>  // warning: C++11 variadic template
    using Vector = bip::vector< T, Allocator<T>>;             // warning: C++11 alias declaration
// this seems to work to get some of the nested <>ness under control.
// But I can't figure out how to create an iterator to this kind of type (see errors below)

// what once were classes are now class templates

template <template<typename...> class Allocator>              // warning: C++11 variadic template
    class Bar
    {
        public:
             Bar( const Allocator<void> & alloc ) : mInts( alloc ) {}
            ~Bar() {}

            void Report( void );

        private:
            Vector< int, Allocator > mInts;
    };

template <template<typename...> class Allocator>              // warning: C++11 variadic template
    class Foo
    {
        public:
             Foo( const Allocator<void> & alloc ) : mBars( alloc ) {}
            ~Foo() {}

            void Report( void );


        private:
            Vector<  Bar<Allocator>, Allocator >  mBars; // hmm, with more complex structures this is going 
                                                         // to get unmanageably< nested< very< quickly > > > ...

    };


// Define allocator templates

template <typename T>
    using HeapAllocator  = std::allocator<T>; // warning: C++11 alias declaration

template <typename T> 
    using ShmemAllocator = bip::allocator<T, bip::managed_shared_memory::segment_manager>; // warning: C++11 alias declaration

// Define two class variants: one for use on the heap and one for use in shared memory

using HeapFoo  = Foo< HeapAllocator  >; // warning: C++11 alias declaration
using ShmemFoo = Foo< ShmemAllocator >; // warning: C++11 alias declaration

// Try to define methods (unsuccessful so far because of the iterators,
// but they compile OK if the function bodies are left empty):

template <template<typename...> class Allocator>              // warning: C++11 variadic template
    void
    Bar< Allocator >::Report( void )
    {
        std::cout << "[";
        Vector< int, Allocator >::iterator it;
// ERROR:     ^~~~~ expected ';' after expression
        for( it = mInts.begin(); it += mInts.end(); it++ )
            std::cout << ( it == mInts.begin() ? "" : ", " ) << *it;
        std::cout << "]\n";
    }

template <template<typename...> class Allocator>              // warning: C++11 variadic template
    void
    Foo< Allocator >::Report( void )
    {
        Vector< Bar< Allocator >, Allocator >::iterator it;
// ERROR:     ^~~~~ expected ';' after expression
        for( 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;
    bip::managed_shared_memory   seg( bip::create_only, "MySharedMemory", 65536 );

    ShmemAllocator< void > shalloc( seg.get_segment_manager() );
    HeapAllocator<  void > halloc;

    HeapFoo  foo1( halloc  );
    ShmemFoo foo2( shalloc );
    foo1.Report();
    foo2.Report();  
}

1 个答案:

答案 0 :(得分:2)

好的,您已经遇到了频繁讨厌的Edgecase,模板模板参数不是C ++中的一等公民(你无法传递它们/ typedef它们):

我们该怎么办?

  1. <强> allocator::rebind<T>

    分配器有重新绑定机制,我敢说正是因为这个原因。因此,您可以传递alloc<void>,就好像它是开放模板一样,因为您可以通过Alloc::rebind<T>::other始终从那里获取兄弟分配器类型。

  2. 除此之外,分配器通常具有执行此重新绑定的转换构造器这一事实,您不需要在使用分配器的许多地方过于具体

  3. 在c ++ 11中,引入了scoped_allocator以避免必须在许多将执行元素内部构造的地方手动传递allocator个实例(例如{{1} }})。

    有适当的库魔法,它会自动将容器的emplace_back中的分配器实例添加为最后一个构造函数参数(默认情况下)。 Boost Container库已将scoped_allocator概念向后移植到c ++ 03,因此您可以使用它。

  4. 这里有一个完整的示例,向您展示如何解决您遇到的问题,以及如何将基于堆的scoped_allocator_adaptor实例与共享内存Bar实例混合:

    Foo

    由于上面提到的foo2.add(bar1); // this works because of ... MAGIC! 而有效。

    <强> Live On Coliru

    scoped_allocator

    打印:

    #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();
    }