【问题标题】:making non-shared copies of boost::interprocess shared memory objects制作 boost::interprocess 共享内存对象的非共享副本
【发布时间】:2015-02-14 00:39:18
【问题描述】:

我已经实现了各种设计用于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;

};

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

或者有什么不错的捷径?我在想象某种特定的allocator 实例,我可以将其传递给Foo(),因此将传递给IndexVector 构造函数,两者都将其识别为“在非共享内存中分配” ”。这样的事情存在吗?是否有用于管理普通非共享内存的“虚拟段管理器”?或者有没有其他方法可以解决这个问题?

我希望得到与 C++03 兼容的答案,尽管我也有兴趣学习 C++11+ 的做事方式。

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

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

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

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

【问题讨论】:

  • 另请参阅 followup 以获取相关想法的欺骗问题
  • 嗯,是的,我看到了那些线程。我了解您对第一个答案的语言/概念,但在后续行动中遇到了困难,尤其是 OP 的评论和对它的回答(“推断函数模板中的参数,并分派到您专门针对不同的函数对象实际参数类型”完全超出了我的理解水平)。在第一个中,似乎工作必须手动和步行完成,逐个成员复制:这是我希望通过“虚拟”部门经理避免的事情(但我想不存在这样的事情)。
  • 在第二个线程中,该解决方案似乎非常特定于容器类(即那些实现 assign、begin 和 end 的容器类)。我想我需要实现特定于分配的方法来完成我的Foo&lt;Allocator&gt; 和Foo&lt;std::allocator&gt; 之间的转换工作。
  • 关于理解第一个线程,我说得太早了。 “为分配器类模板模板化”和template &lt;template&lt;typename...&gt; class Allocator&gt; 需要进行一些研究才能了解这里发生了什么。
  • 重新开放:对于更复杂的类,甚至无法进行复制操作。

标签: c++ boost shared-memory


【解决方案1】:

好的,您遇到了模板模板参数不是 C++ 中的一等公民(您不能将它们传递/typedef 它们)的常见问题:

我们该怎么办?

  1. allocator::rebind&lt;T&gt;

    分配器有一个重新绑定机制,我敢说正是因为这个。所以你可以传递一个alloc&lt;void&gt;,就好像它是打开的模板一样,因为你总是可以通过Alloc::rebind&lt;T&gt;::other从那里得到一个兄弟分配器类型。

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

  3. 在 c++11 中,引入了 scoped_allocators 以避免在许多将进行元素内部构造的地方(例如 emplace_back)手动传递 allocator 实例。

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

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

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

由于上面提到的scoped_allocator 而起作用。

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

打印:

Heap based storage: 
[42, 2, -99]

Shared memory storage: 
[43, 3, -98]
[42, 2, -99]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 2020-10-19
    • 2018-08-04
    • 2012-09-06
    • 1970-01-01
    • 2019-01-29
    相关资源
    最近更新 更多