使得boost ::进程间共享内存对象的非共享副本 [英] making non-shared copies of boost::interprocess shared memory objects
问题描述
我已经实现了被设计为在 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他们):
我们该怎么办?
-
allocator :: rebind< T>
分配者有重新绑定机制,我敢于正是因为这个。所以你可以传递一个
alloc< void>
,就像它是开放的模板,因为你总是可以从那里到兄弟分配器类型通过添加到这一事实,分配器通常有转换构造函数做这个在许多地方,您不需要过分明确地在c ++ 11,
scoped_allocator $中接受分配器
- 已经引入了c $ c>,以避免在多个地方手动传递
allocator
实例,这些地方将执行元素的内部构造(例如emplace_back
)。
有一个库魔术会自动从容器的
scoped_allocator
添加allocator实例作为最后一个构造函数参数默认)。 Boost容器库已将scoped_allocator_adaptor
概念转移到c ++ 03,以便您可以使用它。
这里有一个完整的示例,显示了如何解决您遇到的问题,以及如何混合基于堆的 Bar
实例与共享内存 Foo
实例:
foo2.add ); //这个作品因为... MAGIC!
由于上述 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>
命名空间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:
- boost::interprocess Containers of containers NOT in shared memory
- boost::interprocess Containers of containers NOT in shared memory copy
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?
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 doingAlloc::rebind<T>::other
.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
in c++11,
scoped_allocator
s have been introduced to avoid having to manually passallocator
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 thescoped_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.
#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屋!