Reputation: 692
This is a two part question illustrated by the following code:
#include <memory>
#include <vector>
#include <boost/pool/object_pool.hpp>
struct Foo {
Foo(int i) : _i(i) {}
void* operator new(size_t) = delete; // ***
int _i;
};
using FooHandle = std::unique_ptr<Foo>;
struct Bar {
Foo* addFoo(int i)
{
Foo* ptr = new (_fooPool.malloc()) Foo(i); // 111
return FooHandle(ptr,
&boost::object_pool<Foo,
boost::default_user_allocator_new_delete>::destroy); // 222
}
boost::object_pool<Foo> _fooPool;
};
I am trying to ensure that
Using the Visual Studio compiler I run into following problems
If I delete the default operator new (line marked with ***) then placement new (line marked with 111) does not compile. Am I doing something wrong or is it a VS limitation?
I imagine that in order to make a proper unique_ptr that was allocated in a pool I need to supply access to the pool deleter. The line marked as 222 is my attempt to do just that. The compiler does not accept it either. What is the correct syntax?
Upvotes: 4
Views: 1602
Reputation: 109119
boost::object_pool::destroy
is a non-static
member function, so it needs to be invoked on an object_pool
instance. Furthermore, the deleter is a part of a unique_ptr
's type, so your FooHandle
alias must be declared accordingly. Also, your addFoo()
function is returning a Foo*
when it should be returning a FooHandle
instead.
using FooHandle = std::unique_ptr<Foo, std::function<void(Foo *)>>;
FooHandle addFoo(int i)
{
Foo* ptr = new (_fooPool.malloc()) Foo(i);
return FooHandle(ptr, std::bind(&boost::object_pool<Foo>::destroy,
&_fooPool, std::placeholders::_1));
}
The problem with the new
expression is that your delete
d new
overload is hiding the global placement new
operator. The code compiles if you also overload the placement version.
static void* operator new(size_t count, void *p)
{
return ::operator new(count, p);
}
Alternatively, explicitly call the global operator new
Foo* ptr = ::new (_fooPool.malloc()) Foo(i);
Are you sure you want to bother with the deleted new
overload? Doing that doesn't stop someone from using ::new Foo(10)
to allocate a Foo
outside of your object pool.
As T.C. mentions in the comments, using std::function
to package the deleter is far from ideal. One way to avoid this is to create a functor type that stores a reference to the object_pool
instance, and then calls destroy
on that instance.
struct FooDeleter
{
FooDeleter(boost::object_pool<Foo>& pool)
: pool(&pool)
{}
void operator()(Foo *p)
{
pool->destroy(p);
}
boost::object_pool<Foo> *pool;
};
using FooHandle = std::unique_ptr<Foo, FooDeleter>;
FooHandle addFoo(int i)
{
Foo* ptr = _fooPool.malloc();
if(ptr)
{
return FooHandle(::new (ptr) Foo(i), FooDeleter(_fooPool));
}
else
{
return FooHandle(nullptr, FooDeleter(_fooPool)); // or throw exception etc
}
}
Upvotes: 3