I commonly come across the need to create arrays or vectors of polymorphic objects. I'd usually prefer to use references, rather than smart pointers, to the base class because they tend to be simpler.
Arrays and vectors are forbidden from containing raw references, and so I've tended to use smart pointers to the base classes instead. However, there is also the option to use std::reference_wrapper instead: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
From what I can tell from the documentation, this is what one of its intended uses is, but when the topic of arrays containing polymorphic objects comes up, the common advice seems to be to use smart pointers rather than std::reference_wrapper.
My only thought is that smart pointers may be able to handle the lifetime of the object a little neater?
TL:DR; Why are smart pointers, such as std::unique_ptr seemingly preferred over std::reference_wrapper when creating arrays of polymorphic objects?
In very simple terms:
unique_ptr is the owner of the object. It manages the lifetime of the owned object
reference_wrapper wraps a pointer to an object in memory. It does NOT manage the lifetime of the wrapped object
You should create an array of unique_ptr (or shared_ptr) to guarantee the release of the object when it's not needed anymore.
If you are sufficiently motiviated, you can write a poly_any<Base> type.
A poly_any<Base> is an any restricted to only storing objects that derive from Base, and provides a .base() method that returns a Base& to the underlying object.
A very incomplete sketch:
template<class Base>
struct poly_any:private std::any
{
using std::any::reset;
using std::any::has_value;
using std::any::type;
poly_any( poly_any const& ) = default;
poly_any& operator=( poly_any const& ) = default;
Base& base() { return get_base(*this); }
Base const& base() const { return const_cast<Base const&>(get_base(const_cast<poly_any&>(*this))); }
template< class ValueType,
std::enable_if_t< /* todo */, bool > =true
>
poly_any( ValueType&& value ); // todo
// TODO: sfinae on ValueType?
template< class ValueType, class... Args >
explicit poly_any( std::in_place_type_t<ValueType>, Args&&... args ); // todo
// TODO: sfinae on ValueType?
template< class ValueType, class U, class... Args >
explicit poly_any( std::in_place_type_t<ValueType>, std::initializer_list<U> il,
Args&&... args ); // todo
void swap( poly_any& other ) {
static_cast<std::any&>(*this).swap(other);
std::swap( get_base, other.get_base );
}
poly_any( poly_any&& o ); // todo
poly_any& operator=( poly_any&& o ); // todo
template<class ValueType, class...Ts>
std::decay_t<ValueType>& emplace( Ts&&... ); // todo
template<class ValueType, class U, class...Ts>
std::decay_t<ValueType>& emplace( std::initializer_list<U>, Ts&&... ); // todo
private:
using to_base = Base&(*)(std::any&);
to_base get_base = 0;
};
Then you just have to intercept every means of putting stuff into the poly_any<Base> and store a get_base function pointer:
template<class Base, class Derived>
auto any_to_base = +[](std::any& in)->Base& {
return std::any_cast<Derived&>(in);
};
Once you have done this, you can create a std::vector<poly_any<Base>> and it is a vector of value types that are polymorphically descended from Base.
Note that std::any usually uses the small buffer optimization to store small objects internally, and larger objects on the heap. But that is an implementation detail.
Basically, a reference_wrapper is a mutable reference: Like a reference, it must not be null; but like a pointer, you can assign to it during its lifetime to point to another object.
However, like both pointers and references, reference_wrapper does not manage the lifetime of the object. That's what we use vector<uniq_ptr<>> and vector<shared_ptr<>> for: To ensure that the referenced objects are properly disposed off.
From a performance perspective, vector<reference_wrapper<T>> should be just as fast and memory efficient as vector<T*>. But both of these pointers/references may become dangling as they are not managing object lifetime.
Let's try the experiment:
#include <iostream>
#include <vector>
#include <memory>
#include <functional>
class Base {
public:
Base() {
std::cout << "Base::Base()" << std::endl;
}
virtual ~Base() {
std::cout << "Base::~Base()" << std::endl;
}
};
class Derived: public Base {
public:
Derived() {
std::cout << "Derived::Derived()" << std::endl;
}
virtual ~Derived() {
std::cout << "Derived::~Derived()" << std::endl;
}
};
typedef std::vector<std::reference_wrapper<Base> > vector_ref;
typedef std::vector<std::shared_ptr<Base> > vector_shared;
typedef std::vector<std::unique_ptr<Base> > vector_unique;
void fill_ref(vector_ref &v) {
Derived d;
v.push_back(d);
}
void fill_shared(vector_shared &v) {
std::shared_ptr<Derived> d=std::make_shared<Derived>();
v.push_back(d);
}
void fill_unique(vector_unique &v) {
std::unique_ptr<Derived> d(new Derived());
v.push_back(std::move(d));
}
int main(int argc,char **argv) {
for(int i=1;i<argc;i++) {
if(std::string(argv[i])=="ref") {
std::cout << "vector" << std::endl;
vector_ref v;
fill_ref(v);
std::cout << "~vector" << std::endl;
} else if (std::string(argv[i])=="shared") {
std::cout << "vector" << std::endl;
vector_shared v;
fill_shared(v);
std::cout << "~vector" << std::endl;
} else if (std::string(argv[i])=="unique") {
std::cout << "vector" << std::endl;
vector_unique v;
fill_unique(v);
std::cout << "~vector" << std::endl;
}
}
}
running with argument shared:
vector
Base::Base()
Derived::Derived()
~vector
Derived::~Derived()
Base::~Base()
running with argument unique
vector
Base::Base()
Derived::Derived()
~vector
Derived::~Derived()
Base::~Base()
running with argument ref
vector
Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()
~vector
Explanation:
shared: Memory is shared by different parts of the code. In the example, the Derived object is first owned by the d local var in the function fill_shared() and by the vector. When the code exits the scope of the function object is still owned by the vector and only when the vector goes finally away, the object is deleted
unique: Memory is owned by the unique_ptr. In the example, the Derived object is first owned by the d local var. However it must be moved into the vector, transferring the ownership. Same as before, when the only owner goes away, the object gets deleted.
ref: There's no owning semantics. The object is created as a local variable of the fill_ref() function, and the reference to the object can be added to the vector. However, the vector does not own the memory and when the code goes out of the fill_ref() function, the object goes away, leaving the vector pointing to unallocated memory.
Related
Assume I have a templated MemoryPool class a function create(...) (which returns a pointer to a newly allocated object of type T) and a function destroy(T*) (which destroys and returns the memory back to the pool).
I would like to create a std::unique_ptr that "owns" the pointer created by the pool and returns the pointer to the pool, thus requiring a custom deleter.
The problem is, how do I make this work if the pool contains concrete objects and I want to pass around a std::unique_ptr to an abstract interface of this object.
Here is an example that doesn't compile:
#include <iostream>
#include <memory>
#include <functional>
#include <utility>
template <typename T>
class MemoryPool {
public:
template <typename ... ARGS>
T* create(ARGS&&... args) {
std::cout << "MemoryPool::create()" << std::endl;
return new T(std::forward<ARGS>(args)...);
}
void destroy(T* ptr) {
std::cout << "MemoryPool::destroy()" << std::endl;
delete ptr;
}
};
class ITest {
public:
ITest() {
std::cout << "ITest::ITest()" << std::endl;
}
virtual ~ITest() {
std::cout << "ITest::~ITest()" << std::endl;
}
virtual void sayHello() = 0;
};
class Test :public ITest {
public:
Test() {
std::cout << "Test::Test()" << std::endl;
}
~Test() {
std::cout << "Test::~Test()" << std::endl;
}
void sayHello() override {
std::cout << "Test says hello" << std::endl;
}
};
class ITestOwner {
public:
ITestOwner(std::unique_ptr<ITest> ptr) :
_ptr(std::move(ptr))
{
std::cout << "ITestOwner::ITestOwner()" << std::endl;
}
~ITestOwner() {
std::cout << "ITestOwner::~ITestOwner()" << std::endl;
}
void sayHello() { _ptr->sayHello(); }
private:
std::unique_ptr<ITest> _ptr;
};
int main() {
MemoryPool<Test> pool;
std::unique_ptr<Test, std::function<void(Test*)>> ptr(pool.create(), [&pool](Test* ptr){
std::cout << "Custom Deleter" << std::endl;
pool.destroy(ptr);
});
ITestOwner owner(std::move(ptr));
owner.sayHello();
return 0;
}
Keep in mind that, my real MemoryPool class would actually act as a normal memory pool and not use new/delete as I have done.
In this example, ITestOwner should take over ownership of the std::unique_ptr to a ITest abstract object. Then, when ITestOwner is destroyed, the smart pointer will be destroyed, and the Test object should be returned to the memory pool.
Is there a way to accomplish this?
The code doesn't compile because std::unique_ptr<ITest> is used by ITestOwner while you to forward it std::unique_ptr<Test, std::function<void(Test*)>>. It obviously doesn't compile because std::unique_ptr<ITest> calls delete on ITest instead of calling some complex arbitrary function.
You'd need to use unique_ptr<ITest, function<void(ITest*)>> for it to work and in addition add some unsighty conversion code from function<void(Test*)> to function<void(ITest*)>... I'd say this is simply not good. unique_ptr is designed to be simple and efficient - the destructor is supposed to wrap basic functionality but it isn't convenient enough for complicated purposes.
Basically, unique_ptr is not designed for this task. It is supposed to be lightweight and you already use heavy functionality like std::function that ruins the whole purpose. Instead, you can use shared_ptr which type erases the deleter and hides it - so you'd need nothing to worry about. If you want to still restrict user to unique ownership you can surely find other 3rd party open source libraries that implement the smart pointer you want - there are lots of them.
simple multi-inheritance
struct A {};
struct B {};
struct C : A, B {};
or virtual inheritance
struct B {};
struct C : virtual B {};
Please note types are not polymorphic.
Custom memory allocation:
template <typedef T, typename... Args>
T* custom_new(Args&& args...)
{
void* ptr = custom_malloc(sizeof(T));
return new(ptr) T(std::forward<Args>(args)...);
}
template <typedef T>
void custom_delete(T* obj)
{
if (!obj)
return obj;
void* ptr = get_allocated_ptr(obj); // here
assert(std::is_polymorphic_v<T> || ptr == obj);
obj->~T();
custom_free(ptr); // heap corruption if assert ^^ failed
}
B* b = custom_new<C>(); // b != address of allocated memory
custom_delete(b); // UB
How can I implement get_allocated_ptr for non polymorphic types? For polymorphic types dynamic_cast<void*> does the job.
Alternatively I could check that obj is a pointer to a base class as deleting a non polymorphic object by a pointer to base class is UB. I don't know how to do this or if it's possible at all.
operator delete properly deallocates memory in such cases (e.g. VC++), though standard says it's UB. How does it do this? compiler-specific feature?
You actually have a more serious problem than getting the address of the full object. Consider this example:
struct Base
{
std::string a;
};
struct Derived : Base
{
std::string b;
};
Base* p = custom_new<Derived>();
custom_delete(p);
In this example, custom_delete will actually free the correct address (static_cast<void*>(static_cast<Derived*>(p)) == static_cast<void*>(p)), but the line obj->~T() will invoke the destructor for Base, meaning that the b field is leaked.
So Don't Do That
Instead of returning a raw pointer from custom_new, return an object that is bound to the type T and that knows how to delete it. For example:
template <class T> struct CustomDeleter
{
void operator()(T* object) const
{
object->~T();
custom_free(object);
}
};
template <typename T> using CustomPtr = std::unique_ptr<T, CustomDeleter<T>>;
template <typename T, typename... Args> CustomPtr<T> custom_new(Args&&... args)
{
void* ptr = custom_malloc(sizeof(T));
try
{
return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
}
catch (...)
{
custom_free(ptr);
throw;
}
}
Now it's impossible to accidentally free the wrong address and call the wrong destructor because the only code that calls custom_free knows the complete type of the thing that it's deleting.
Note: Beware of the unique_ptr::reset(pointer) method. This method is extremely dangerous when using a custom deleter since the onus is on the caller to supply a pointer that was allocated in the correct way. The compiler can't help if the method is called with an invalid pointer.
Passing Around Base Pointers
It may be that you want to both pass a base pointer to a function and give that function responsibility for freeing the object. In this case, you need to use type erasure to hide the type of the object from consumers while retaining knowledge of its most derived type internally. The easiest way to do that is with a std::shared_ptr. For example:
struct Base
{
int a;
};
struct Derived : Base
{
int b;
};
CustomPtr<Derived> unique_derived = custom_new<Derived>();
std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
Now you can freely pass around shared_base and when the final reference is released, the complete Derived object will be destroyed and its correct address passed to custom_free. If you don't like the semantics of shared_ptr, it's fairly straightforward to create a type erasing pointer with unique_ptr semantics.
Note: One downside to this approach is that the shared_ptr requires a separate allocation for its control block (which won't use custom_malloc). With a little more work, you can get around that. You'd need to create a custom allocator that wraps custom_malloc and custom_free and then use std::allocate_shared to create your objects.
Complete Working Example
#include <memory>
#include <iostream>
void* custom_malloc(size_t size)
{
void* mem = ::operator new(size);
std::cout << "allocated object at " << mem << std::endl;
return mem;
}
void custom_free(void* mem)
{
std::cout << "freeing memory at " << mem << std::endl;
::operator delete(mem);
}
template <class T> struct CustomDeleter
{
void operator()(T* object) const
{
object->~T();
custom_free(object);
}
};
template <typename T> using CustomPtr = std::unique_ptr<T, CustomDeleter<T>>;
template <typename T, typename... Args> CustomPtr<T> custom_new(Args&&... args)
{
void* ptr = custom_malloc(sizeof(T));
try
{
return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
}
catch (...)
{
custom_free(ptr);
throw;
}
}
struct Base
{
int a;
~Base()
{
std::cout << "destroying Base" << std::endl;
}
};
struct Derived : Base
{
int b;
~Derived()
{
std::cout << "detroying Derived" << std::endl;
}
};
int main()
{
// Since custom_new has returned a unique_ptr with a deleter bound to the
// type Derived, we cannot accidentally free the wrong thing.
CustomPtr<Derived> unique_derived = custom_new<Derived>();
// If we want to get a pointer to the base class while retaining the ability
// to correctly delete the object, we can use type erasure. std::shared_ptr
// will do the trick, but it's easy enough to write a similar class without
// the sharing semantics.
std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
// Notice that when we release the shared_base pointer, we destroy the complete
// object.
shared_base.reset();
}
You can do that only using dynamic_cast and static type of T has to be polymorphic. Otherwise look at this code:
struct A { int a; };
struct B { int b; };
struct C : A, B {};
B *b1 = new C, *b2 = new B;
If you try to delete by pointer to B, there is no way to know if b1 or b2 needs to be adjusted to get_allocated_ptr. One way or another you need B to be polymorphic to get pointer to most derived object.
What about a virtual interface that all structs inherit from, which returns the pointer at which the object was allocated? I had to make some changes to make the code compile. Both the multiple inheritance and virtual inheritance cases work:
#include <iostream>
#include <type_traits>
#include <cassert>
struct H {
public:
void* getHeader() { return header; }
void setHeader(void* ptr) { header = ptr; }
private:
void* header;
};
// multiple inheritance case
//struct A : public virtual H { int a;};
//struct B : public virtual H { int b;};
//struct C : A, B { };
// virtual inheritance case
struct B : public virtual H { int b; };
struct C : virtual B {};
template <typename T, typename ...Args>
T* custom_new(Args&&... args) {
void* ptr = malloc(sizeof(T));
T* obj = new(ptr) T(std::forward<Args>(args)...);
obj->setHeader(ptr);
return obj;
}
template <typename T>
void* get_allocated_ptr(T* obj) {
return obj->getHeader();
}
template <typename T>
void custom_delete(T* obj) {
void* ptr = get_allocated_ptr(obj); // here
// assert(std::is_polymorphic<T>::value || ptr == obj); // had to comment
obj->~T();
free(ptr); // heap corruption if assert ^^ failed
}
using namespace std;
int main(int argc, char *argv[]) {
C* c = custom_new<C>(); // b != address of allocated memory
std::cout << "PTR \t\t= " << c << std::endl;
auto b = static_cast<B*>(c);
std::cout << "CAST PTR \t= " << b << std::endl;
std::cout << "ALLOCATED PTR \t= " << get_allocated_ptr(b) << std::endl;
custom_delete(b); // UB
}
You can run this with either hierarchy, and the output is something like
PTR = 0x7f9fd4d00b90
CAST PTR = 0x7f9fd4d00b98
ALLOCATED PTR = 0x7f9fd4d00b90
although in the multiple inheritance case the pointers differ by 16 bits rather than 8 (because of the two integers).
This implementation could be improved by using templates to enable custom_new and the other functions only for structs inheriting from the H interface.
Reference code:
#include <vector>
#include <iostream>
class Func {
public:
virtual void call() {
std::cout<< "Func -> call()" << std::endl;
}
};
class Foo : public Func {
public:
void call() {
std::cout<< "Foo -> call()" << std::endl;
}
};
class Bar : public Func {
public:
void call() {
std::cout<< "Bar -> call()" << std::endl;
}
};
int main(int argc, char** argv) {
std::vector<Func> functors;
functors.push_back( Func() );
functors.push_back( Foo() );
functors.push_back( Bar() );
std::vector<Func>::iterator iter;
for (iter = functors.begin(); iter != functors.end(); ++iter)
(*iter).call();
}
When run that code, it produces the following output on my computer:
$ ./test
Func -> call()
Func -> call()
Func -> call()
Would there be any way to ensure that the correct virtual function is called in this instance? I'm new at C++ but my best guess is that here:
(*iter).call();
It's being cast to a Func object. Is this correct?
You should use a shared_ptr or unique_ptr to hold the elements of a collection of polymorphic type.
As your code is written now the Foo and Bar instances are cooerced (copy constructed) into an instance of type Func to fit into the vector. (The reason is that vector stores its elements immediately by fixed-sized value for performance, however polymorphic subclasses are of a arbitrarily larger size unknown at compile-time, so it can only store the base class.)
This is better:
int main(int argc, char** argv) {
vector<shared_ptr<Func>> functors;
functors.push_back( make_shared<Func>() );
functors.push_back( make_shared<Foo>() );
functors.push_back( make_shared<Bar>() );
for (auto functor : functors)
functor->call();
}
In the above a reference-counted pointer is used to implicitly share the hetrogeneous subclasses of Func in the vector. (This indirection allows arbitrarily sized subclasses of Func to be stored by address indirection.)
Also, you may want to take a look at std::function and std::bind, rather than rolling your own functor type.
Another thing to look at would be perfect forwarding and varadic templates.
update: For old compiler:
int main(int argc, char** argv) {
vector<std::tr1::shared_ptr<Func> > functors;
functors.push_back( std::tr1::make_shared<Func>() );
functors.push_back( std::tr1::make_shared<Foo>() );
functors.push_back( std::tr1::make_shared<Bar>() );
for (size_t i = 0; i < functors.size(); ++i)
functors[i]->call();
}
the vector only holds the type Func by value, meaning that all your temporals Foo and Bar are sliced and casted to the base Func type
you need to change to something like std::vector< Func* > and dynamically allocate the derived classes instances in order for polymorphic dispatch to work
If you are completely sure that you won't pass this vector to other functions after this function returns, as an optimization you might want to allocate the instances in the stack:
std::vector< Func* > v;
Bar b;
Foo f;
v.push_back( &b);
v.push_back( &f);
your std::vector is storing Func objects - this means that when you call
functors.push_back( Foo() );
functors.push_back( Bar() );
you're creating Foo and Bar objects, then "slicing" those objects as they are copied into Func objects.
If you'd like to use Foo and Bar polymorphically, then a more typical pattern would be to store a vector of some pointer type (Preferably not "raw" pointers though), for example
std::vector< std::unique_ptr<Func> >
std::vector< std::shared_ptr<Func> >
Or, if you really have to.. (But only if you're using an older compiler which doesn't have shared_ptr or unique_ptr)
std::vector< Func* >
In C++ polymorphism works only with pointers and references, while the vector stores directly instances of objects. When you call push_back the copy constructor of Func is called, which builds the Func object that is stored inside the vector.
This is called object slicing, you can learn more about it with a quick search in StackOverflow.
The solution would be to store pointers (or, even better, smart pointers) to your objects, which should be allocated elsewhere (probably on the heap).
In general, instances of subclasses may be larger than those of their superclass, so you should not expect the subclasses to fit into your vector's slot.
And push_back probably will call internally a copy constructor (of the Func class, since you have vector<Func>) so the internal vector slots are indeed Func not of some other classes.
Your problem is that you have a vector of Func, but methods are called polymorphically only through references or pointers.
there is a very simple way of making vector without using pointers by creating child class which will inherit Class A and Class B
#include <iostream>
#include <vector>
using namespace std;
struct BASE {
string name = "Base class ";
};
struct A : BASE {
string a = " class a ";
};
struct B : BASE {
string b = " class b ";
};
struct C : A, B{
//using base name from A or B// yeah is hardcoded
string name = A::name;
};
int main()
{
C c;
c.a = "s";
c.b = "h";
vector<C> vec;
vec.push_back(c);
c.b = "j";
vec.push_back(c);
cout << vec[0].a << endl;
cout << vec[0].b << endl;
cout << vec[0].name << endl;
cout << vec[1].a << endl;
cout << vec[1].b << endl;
cout << vec[1].name << endl;
return 0;
}
Lets say I have heap allocated A*, which I want to pass as argument to boost::bind.
boost::bind is saved for later processing in some STL like container of boost::functions's.
I want to ensure A* will be destroyed at destruction of the STL container.
To demostrate:
A* pA = new A();
// some time later
container.push_back(boost::bind(&SomeClass::HandleA, this, pA);
// some time later
container is destroyed => pA is destroyed too
How can it be done?
EDIT
Maybe what I want is not that realistic.
I have raw pointer and function which receives the raw pointer. The call is delayed by means of boost::bind. At this point I want automatic memory management in case boost::bind want executed. I'm lazy, so I want to use "ready" smart-pointer solution.
std::auto_ptr looks like a good candidate, however ...
auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, pAutoA);
doesn't compile (see here)
auto_ptr<A> pAutoA(pA);
container.push_back(boost::bind(&SomeClass::HandleA, this, boost::ref(pAutoA));
pAutoA is destroyed, deleting underlying pA.
EDIT 02
In the mentioned container I will need to store misc "callbacks" with different arguments. Some of them are raw pointers to object. Since the code is old, I not always can change it.
Writing own wrapper for storing callbacks in container is last resort (while maybe the only one), hence bounty.
The idea of #pmjordan was already going in the right direction. You replied that you can't use shared_ptr, because you can't take ownership back from it once constructed. But that is not entirely correct: with shared_ptr's custom deleter mechanism, you can. This is how:
Assume these toy defintions for your A and f(A*):
struct A {
~A() { std::cout << "~A()" << std::endl; }
};
void f( A * a ) {
std::cout << "in f(A*)" << std::endl;
delete a;
}
Write a deleter that can be "switched off":
struct opt_delete {
bool m_delete;
opt_delete() : m_delete( true ) {}
template <typename T>
void operator()( T * t ) {
if ( m_delete ) delete t;
}
};
Then you can write a take() function that takes ownership of the shared_ptr payload again:
template <typename T>
T * take( const boost::shared_ptr<T> & sp ) {
opt_delete * d = boost::get_deleter<opt_delete>( sp );
assert( d );
assert( d->m_delete == true );
d->m_delete = false;
return sp.get();
}
(this will leave the payload in the remaining shared_ptr instances, but for your case, that's ok, and the assert()s cover the cases when it's not).
Now you can manually wrap f(A*) like this:
void f_sp( const boost::shared_ptr<A> & a ) {
f( take( a ) );
}
And finally, test the two scenarios:
int main( int argc, char * argv[] ) {
const boost::shared_ptr<A> a( new A, opt_delete() );
const boost::function<void()> func =
boost::bind( &f_sp, a );
if ( argc >= 2 && *argv[1] == '1' ) // call 'func'
func();
else
; // don't
return 0;
}
Executing the test program with a 1 argument will print
in f(A*)
~A()
and without (or any other argument), it will print
~A()
You can extend the test harness to put func into a container first, but it'll still be safe. The only thing that isn't safe in the case is calling the func copies more than once (but then you'll trigger the second assertion in take()).
EDIT: Note that this mechanism isn't thread-safe. To make it thread-safe, you need to supply opt_delete with a mutex to synchronise operator() with take().
I assume you mean you have some function, let's call it f() which takes an A*, which you then proxy with boost::bind? Can you change this function to accept a Boost/TR1 shared_ptr<A> instead? Using a shared_ptr (or, less likely, a C++98 std::auto_ptr) should solve your lifecycle problem.
Alternatively, if you can't change f itself, you could create a wrapper which accepts a shared_ptr<A>, pulls out the raw pointer and calls f with it. If you find yourself writing a lot of these wrappers, you may be able to create a template for generating them, assuming the function signatures are similar.
NB! This is UGLY!
Have just scrateched some proof of concept. Well, it does what requested, as far as I can see - but this stuff relies on const_cast assumption. If you decide to use something like that in your program, be ready to double check all copy constructions happening in your program all the time, and using valgrind to verify nothing is leaked/corrupted.
Trick is in defining you own wrapper class, that ignores const qualifiers and allows auto_ptr ownership transfer from const referenced auto_ptr. This can get crazy if you ll try, for example, copy vector itself.
So be sure to read carefuly about vector copy semantics, auto_ptr ownership transfer semantics and, best of all - just use shared_ptr :)
#include <iostream>
#include <boost/bind.hpp>
#include <algorithm>
#include <vector>
#include <boost/function.hpp>
class parameter_data
{
public:
~parameter_data()
{
std::cout << "~parameter_data()" << std::endl;
}
parameter_data()
{
std::cout << "parameter_data()" << std::endl;
}
};
void f( parameter_data* data )
{
std::cout << "Processing data..." << std::endl;
};
class storage_wrapper
{
private:
boost::function<void()> callable;
std::auto_ptr<parameter_data> data;
public:
storage_wrapper( const storage_wrapper& copy )
{
callable = const_cast< storage_wrapper&>(copy).callable;
data = const_cast< storage_wrapper&>(copy).data;
}
storage_wrapper( parameter_data *adata )
: data( adata )
{
callable = boost::bind( &f, adata );
}
storage_wrapper& operator=( const storage_wrapper& copy)
{
callable = const_cast< storage_wrapper&>(copy).callable;
data = const_cast< storage_wrapper&>(copy).data;
}
void operator()()
{
callable();
}
};
int main()
{
std::cout << "Start of program" << std::endl;
{
std::vector<storage_wrapper> container;
for ( int i = 0; i < 100; i++ )
container.push_back( storage_wrapper( new parameter_data() ) );
for ( int i = 0; i < 100; i++ )
container[i]();
}
std::cout << "End of program" << std::endl;
return 0;
}
It doesn't need to be very complex:
class MyContainer : public std::vector<boost::function<void ()> > {
public:
void push_back(boost::function<void ()> f, A *pA)
{ push_back(f); vec.push_back(pA); }
~MyContainer()
{ int s=vec.size; for(int i=0;i<s;i++) delete vec[i]; }
private:
std::vector<A*> vec;
};
It has one problem that you need to pass it to other functions via MyContainer & instead of std::vector reference, otherwise the original push_back can be called and it allows for cases where you can push_back without providing the A* pointer. Also it has no check for bind parameters to be the same A* object than pA. You can fix that by changing the push_back prototype:
template<class T>
void push_back(T *object, void (T::*fptr)(), A *pA)
{
push_back(boost::bind(fptr, object, pA)); vec.push_back(pA);
}
I have a function where I want a cleanup action done 90% of the time, but in 10% I want some other action to be done.
Is there some way to use some standard scoped control likeshared_ptr<> so that initially it can have one delete action and then later in the function the delete action can be changed?
shared_ptr<T> ptr( new T, std::mem_fun_ref(&T::deleteMe) );
ptr.pn.d = std::mem_fun_ref(&T::queueMe);
Not really - the standard for shared_ptr is written in such a way that the Deleter may be stored by value in control node (a special object that contains the reference counter, holds deleter, tracks weak pointers etc). The deleter is type-erased, but if you know the concrete deleter type somehow, you can use std::get_deleter<Deleter>(ptr). With it you may access the deleter and change its state. Example:
struct A {};
struct deleter {
void operator()(A* a) {delete a; }
int m_state;
};
std::shared_ptr<A> ptr(new A(), deleter{});
std::get_deleter<deleter>(ptr)->m_state = 5;
And if you use just a function pointer for all deleters, then yes you can completely replace it, as all potential deleters use the same signature.
(Yes I know the question is 9 years old, but I've just faced this problem in 2020 and solved it like this. The possible reason for it is wrapping C pointers and objects from legacy code that manage ownership through raw pointers)
I don't think you can change the deleter once the shared_ptr was created.
But why would you do that ? Usually, when you create an object, you know immediatly how it must be destroyed. This is not likely to change.
If you really must do some specific treatments, you still can provide a custom deleter which does special things depending on the required logic.
There is a valid reason to need to change the deleter. Take this for example:
int foo( std::shared_ptr<double>& bar ) {
...
std::shared_ptr<double> p( my_allocator<double>::allocate(), my_deleter<double>() );
bar.swap(p); // this copies the deleter
...
}
int main( int, char** ) {
std::shared_ptr<double> d;
foo( d ); // d now has a new deleter that will be called when it goes out of scope
...
}
In this case the foo() function allocates a double* using some special allocator. It needs to free that memory in a special way also. The caller shouldn't need to know how to free the memory.
#include <iostream>
#include <memory>
#include <functional>
struct A {
~A() {
std::cout << "~A()" << std::endl;
}
};
using DeleterCb = std::function<void(A* p)>;
struct ADeleter {
public:
explicit ADeleter(DeleterCb cb) :
mDeleterCb(cb) {}
ADeleter() = delete;
~ADeleter() = default;
void operator()(A *a) {
mDeleterCb(a);
}
void setDeleterCb(DeleterCb cb) {
mDeleterCb = cb;
}
private:
DeleterCb mDeleterCb;
};
int main() {
auto sp = std::shared_ptr<A>(new A{},
ADeleter([](A *p){
delete p;
std::cout << "deleter_1" << std::endl;
})
);
std::get_deleter<ADeleter>(sp)->setDeleterCb(
[](A *p){
delete p;
std::cout << "deleter_2" << std::endl;
}
);
}
This doesn't make any sense, since there is any number of shared_ptrs managing the ownership of the value. You'd need to modify them all, and that's not feasible. Let's not forget that a control block is an implementation detail, so going "aha, but change it in the control block" won't work.
The delete actions should be controlled by the instance owned by shared_ptr, e.g.
class C {
...
void (C::action*)() { &C::action1 };
void action1();
void action2();
~C() { (this->*action)(); }
};
void test() {
std::shared_ptr<C> a;
a->action = &C::action2;
// action2 gets invoked once `a` falls out of scope
}