使得boost ::进程间共享内存对象的非共享副本 [英] making non-shared copies of boost::interprocess shared memory objects

查看:92
本文介绍了使得boost ::进程间共享内存对象的非共享副本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了被设计为在 boost :: interprocess 共享内存段中使用的各种类。所有的构造函数使用 allocator 引用 - 在我写的定义中有一些明确的(如 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段;
typedef boost :: interprocess :: managed_shared_memory :: segment_manager SegmentManager;
typedef boost :: interprocess :: allocator< void,SegmentManager>分配器;

typedef size_t索引;
typedef boost :: interprocess :: allocator<索引,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& / code>而不是我的共享 IndexVector 类型),并提供它们之间的复制/转换功能?这将是很多工作和很多愚蠢的重复。我可以通过向现有的 Foo 类提供一个替代构造函数来减少重复,但是我不知道如何初始化 IndexVector 没有分配器的会员。



还是有一些不错的快捷方式?我想象某种特定的分配器实例,我可以传递到 Foo(),因此将传递到 IndexVector 构造函数,这将被两者识别为在非共享内存中分配。这样的事情存在吗?是否有一个虚拟段管理器用于管理vanilla非共享内存?或者有其他方法解决这个问题吗?



我希望C ++ 03兼容的答案,尽管我也有兴趣了解C ++ 11 +处理方式。



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





并尝试推广我看到的,有一些成功和一些失败(见下面的列表)。有一些编译器错误,我还没有能够解决,标记为错误 - 特别是我不知道如何实例化方法迭代这些高度元容器的成员。但是有了或者没有那些错误,我还不能看到如何使模板模板成为可维护的解决方案(我的对象,实际上,包含其他复杂对象的容器,其中包含更多的容器,AFAICS复杂的语法超出健全。 ..看到标有hmm的部分。)



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

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

命名空间bip = boost :: interprocess; // warning:C ++ 11别名声明

template< typename T,template< typename ...>类分配器> // warning:C ++ 11 variadic template
using Vector = bip :: vector< T,Allocator T; // warning:C ++ 11别名声明
//这似乎可以控制一些嵌套的<>。
//但我不知道如何创建一个迭代器到这种类型(见下面的错误)

//以前是类是类模板

template< template< typename ...>类分配器> // 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 ...>类分配器> // 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,具有更复杂的结构这是要
//得到不可管理的<嵌套非常<快速> > > ...

};


//定义分配器模板

template< typename T>
使用HeapAllocator = std :: allocator< T> ;; // warning:C ++ 11别名声明

template< typename T>
使用ShmemAllocator = bip :: allocator< T,bip :: managed_shared_memory :: segment_manager> ;; // warning:C ++ 11别名声明

//定义两个类变量:一个用于堆上,一个用于共享内存

使用HeapFoo = Foo< ; HeapAllocator> ;; // warning:C ++ 11别名声明
使用ShmemFoo = Foo< ShmemAllocator> ;. // warning:C ++ 11别名声明

//尝试定义方法(到目前为止,由于迭代器,
//不成功,但是如果函数体留空):

template< template< typename ...>类分配器> // warning:C ++ 11 variadic template
void
Bar< Allocator> :: Report(void)
{
std :: cout<< [;
Vector< int,Allocator> :: iterator it;
//错误:^ ~~~~预期';'表达式后
for(it = mInts.begin(); it + = mInts.end(); it ++)
std: :cout< (it == mInts.begin()?:,)< *它;
std :: cout<< ] \\\
;
}

template< template< typename ...>类分配器> // warning:C ++ 11 variadic template
void
Foo< Allocator> :: Report(void)
{
Vector< Bar< Allocator>,Allocator> :: iterator it;
//错误:^ ~~~~预期';'表达式后
for(it = mBars.begin(); it + = mBars.end(); it ++)
it- > Report();
std :: cout<< \\\
;
}

int main(void)
{
struct shm_remove
{
shm_remove(){bip :: shared_memory_object :: remove MySharedMemory); }
〜shm_remove(){bip :: shared_memory_object :: remove(MySharedMemory); }
} removever;
bip :: managed_shared_memory seg(bip :: create_only,MySharedMemory,65536);

ShmemAllocator< void> shalloc(seg_get_segment_manager());
HeapAllocator< void>哈洛克

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


解决方案

到常见的烦人的模板中,模板模板参数不是C ++中的头等公民(你不能传递他们/ typedef他们):





我们该怎么办?


  1. allocator :: rebind< T>



    分配者有重新绑定机制,我敢于正是因为这个。所以你可以传递一个 alloc< void> ,就像它是开放的模板,因为你总是可以从那里到兄弟分配器类型通过添加到这一事实,分配器通常有转换构造函数做这个在许多地方,您不需要过分明确地在c ++ 11, scoped_allocator


  2. 已经引入了c $ c>,以避免在多个地方手动传递 allocator 实例,这些地方将执行元素的内部构造(例如 emplace_back )。



    有一个库魔术会自动从容器的 scoped_allocator 添加allocator实例作为最后一个构造函数参数默认)。 Boost容器库已将 scoped_allocator_adaptor 概念转移到c ++ 03,以便您可以使用它。


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

  foo2.add ); //这个作品因为... MAGIC! 

由于上述 scoped_allocator



Live在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>

命名空间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; //与uses_allocator / scoped_allocator绑定

//只有在没有默认构造时才需要分配器
Bar(Alloc alloc = Alloc()):mInts(alloc){}

//转换构造函数,所以我们可以在分配器之间转换
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; //我们可以看到对方的mInts
typedef vector< int,Alloc> ints_t;
ints_t mInts;
};

template< typename Alloc> struct Foo {
typedef Alloc allocator_type; //关联与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;
};
}

命名空间堆{
使用VAlloc = std :: allocator< void> ;;

使用Bar = generic :: Bar< VAlloc> ;;
using Foo = generic :: Foo< VAlloc> ;;
}

命名空间共享{
使用VAlloc = boost :: container :: scoped_allocator_adaptor< bip :: allocator< void,bip :: managed_shared_memory :: segment_manager> > ;;

using Bar = generic :: Bar< VAlloc> ;;
using Foo = generic :: Foo& lt; 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()?:,)< *它;
std :: cout<< ] \\\
;
}

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<< \\\
;
}

int main(void){
struct shm_remove {
shm_remove(){bip :: shared_memory_object :: remove(MySharedMemory); }
〜shm_remove(){bip :: shared_memory_object :: remove(MySharedMemory); }
} removever;

///////////////////////////////////
// heap based:
std :: cout<< 基于堆的存储:\\\
;

heap :: Foo foo1;
heap :: Bar bar1;

bar1.add(42);
bar1.add(2);
bar1.add(-99);

foo1.add(bar1);
foo1.Report();

/////////////////////////////////
std :: cout< ;& 共享内存存储:\\\
;
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); //当然这个工程
foo2.add(bar1); //这个作品因为... MAGIC!
foo2.Report();
}

列印:

 基于堆的存储:
[42,2,-99]

共享内存存储:
[43,3,-98]
[42,2,-99]


I have implemented various classes that are designed to be used in boost::interprocess shared memory segments. All their constructors employ allocator<void,segment_manager> references—some explicitly in the definitions I have written (like the Foo constructor below) and some simply because that's what the boost container definition requires, in boost library code that I should not be changing (like the IndexVector below).

#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;

};

Mostly, these objects sit in shared memory. But I sometimes want to create copies of them in non-shared memory. My question is this: do I have to define a whole different class (e.g. Foo_Nonshared) containing different member types (std::vector<Index> instead of my shared IndexVector type) and provide copy/conversion functions between them? That will be a lot of work and a lot of stupid duplication. I could reduce duplication by providing an alternative constructor to the existing Foo class, but then I wouldn't know how to initialize the IndexVector member without an allocator.

Or is there some nice shortcut? I'm imagining some sort of particular allocator instance that I can pass to Foo(), and which will hence be passed on to the IndexVector constructor, which will be recognized by both as meaning "allocate in non-shared memory". Does such a thing exist? Is there a "dummy segment manager" for managing vanilla non-shared memory? Or are there other ways around this problem?

I'm hoping for C++03-compatible answers even though I'm also interested to learn the C++11+ ways of doing things.

Update following question being marked as duplicate: I have read these previous similar questions:

and have tried to generalize what I see there, with some successes and some failures (see listing below). There are a few compiler errors that I haven't been able to resolve, marked ERROR—in particular I cannot figure out how to instantiate methods that iterate over the members of these highly "meta" containers. But with or without those errors, I cannot yet see how to make templates-of-templates into a maintainable solution (my objects, in reality, contain containers of other complex objects, which contain further containers, which AFAICS complicates the syntax beyond sanity... see the part marked "hmm").

I guess, in the end, I might have to re-design to avoid having the same objects in shared and heap memory.

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

解决方案

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):

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]

这篇关于使得boost ::进程间共享内存对象的非共享副本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆