I have implemented various classes that are designed to be used in boost::interprocess
shared memory segments. All their constructors employ allocator<
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
Allocators have a rebind mechanism, I daresay precisely because of this. So you can pass a alloc
as if it is the open template, because you can always get from there to a sibling allocator type by doing Alloc::rebind
.
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 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
#include
#include
#include
namespace bip = boost::interprocess;
namespace generic {
template */ >
using vector = bip::vector::other >;
template 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
Bar(Bar 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 friend struct Bar; // we can see each other's mInts
typedef vector ints_t;
ints_t mInts;
};
template struct Foo {
typedef Alloc allocator_type; // ties in with uses_allocator/scoped_allocator
Foo(Alloc alloc = Alloc()) : mBars(alloc) {}
void Report() const;
template
void add(Bar const& bar) { mBars.emplace_back(bar); }
private:
typedef vector, Alloc> mbars_t;
mbars_t mBars;
};
}
namespace heap {
using VAlloc = std::allocator;
using Bar = generic::Bar;
using Foo = generic::Foo;
}
namespace shared {
using VAlloc = boost::container::scoped_allocator_adaptor >;
using Bar = generic::Bar;
using Foo = generic::Foo;
}
template void generic::Bar::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
void generic::Foo::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]