Getting a Boost::Shared_Ptr for This

Getting a boost::shared_ptr for this

You can derive from enable_shared_from_this and then you can use "shared_from_this()" instead of "this" to spawn a shared pointer to your own self object.

Example in the link:

#include <boost/enable_shared_from_this.hpp>

class Y: public boost::enable_shared_from_this<Y>
{
public:

shared_ptr<Y> f()
{
return shared_from_this();
}
}

int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}

It's a good idea when spawning threads from a member function to boost::bind to a shared_from_this() instead of this. It will ensure that the object is not released.

std::shared_ptr of this

There is std::enable_shared_from_this just for this purpose. You inherit from it and you can call .shared_from_this() from inside the class. Also, you are creating circular dependencies here that can lead to resource leaks. That can be resolved with the use of std::weak_ptr. So your code might look like this (assuming children rely on existence of parent and not the other way around):

class A;
class B;

class A
: public std::enable_shared_from_this<A>
{
public:
void addChild(std::shared_ptr<B> child)
{
children.push_back(child);

// like this
child->setParent(shared_from_this()); // ok
// ^^^^^^^^^^^^^^^^^^
}

private:
// note weak_ptr
std::list<std::weak_ptr<B>> children;
// ^^^^^^^^
};

class B
{
public:
void setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
}

private:
std::shared_ptr<A> parent;
};

Note however, that calling .shared_from_this() requires that this is owned by std::shared_ptr at the point of call. This means that you cannot create such object on stack anymore, and generally cannot call .shared_from_this() from within a constructor or destructor.

Question on converting boost shared pointer to standard shared pointer

The shared pointer created has a destroy function object (deleter) that has state. In particular it has a copy of the boost shared ptr.

The destruction action does nothing, it is the destruction of deleter that cleans up the boost shared ptr.

There is a problem though:

Does the C++ standard fully specify cleanup of the deleter itself? E.g. it might stay around when only weak references remain?
The standard does guarantee a minimum lifetime for the deleter, leaving the possibility that lives longer than that.

Destroying the destruction object is not specified to occur in either the constructor of std::shared_ptr nor the destructor of std::shared_ptr. It would be reasonable to destroy it either after it is called, or when the reference counting block is destroyed - in the second case, we end up with it lasting until the last weak ptr goes away.

From the draft standard:

[Note: It is unspecified whether the pointer remains valid longer than that. This can happen if the implementation doesn’t destroy the deleter until all weak_ptr instances that share ownership with p have been destroyed. — end note]

The possibility to defer destruction of the deleter is clear. So in that case we end up with shared_ptrs that doesn't destruct their element instance when we want it to be called depending on unspecified details of the C++ implementation you run it on. That is a bad idea.

Better Alternative

I would personally go with a similar trick based on the aliasing constructor instead. It is just as simple, has no additional overhead, and the semantics are actually correct:

template<class T>
std::shared_ptr<T>
as_std_shared_ptr(boost::shared_ptr<T> bp)
{
if (!bp) return nullptr;
// a std shared pointer to boost shared ptr. Yes.
auto pq = std::make_shared<boost::shared_ptr<T>>(std::move(bp));
// aliasing ctor. Hide the double shared ptr. Sneaky.
return std::shared_ptr<T>(pq, pq.get()->get());
}

This is much less of a hack than the code from the question, because lifetime
of the boost shared ptr is no longer tied to the (unspecified) lifetime of the deleter instance.

boost::shared_ptr from pointer

If you have a shared_ptr managing a pointer and then create another shared_ptr managing the same pointer (NOT copying the original shared_ptr), you end up with two managers for the same resource. When one of the two reach a reference count of 0, it will delete the object and the other shared_ptr will point to deleted memory with all that follows.

Get point-to type of boost shared_ptr

Documentation says there's a member typedef called element_type:

This example program runs fine (assertion passes):

#include <boost/shared_ptr.hpp>
#include <boost/type_traits.hpp>
#include <cassert>

int main()
{
bool b = boost::is_same<boost::shared_ptr<int>::element_type, int>::value;
assert(b);
}

Given the typedef you've already declared, you can use it like this:

typedef queue_ptr::element_type elem_type;
v.push_back( boost::make_shared<elem_type>( /* args for ctor */ ) );

Using boost::function with a parameter to shared pointer to derived class

There's no co-variance for function prototypes. Different signatures are simply that: different types.

In this case you'd need to wrap the function with a converting wrapper.

Let's create a few facility definitions:

using result_t = int;
struct A { };
struct B : A { };

typedef boost::shared_ptr<A> APtr;
typedef boost::shared_ptr<B> BPtr;

result_t f1(APtr) { return 1; }
result_t f2(BPtr) { return 2; }

typedef boost::function <result_t(APtr const&)> funOfA;
typedef boost::function <result_t(BPtr const&)> funOfB;

Now wrapping funOfB as a funOfA would look like this:

funOfA wrapFunOfB(const funOfB f) {
struct {
funOfB _f;
result_t operator()(APtr const& a) const {
return _f(boost::static_pointer_cast<B>(a));
}
} wrap { f };

return wrap;
}

Now you can easily write:

int main() {
F(f1);
F(wrapFunOfB(f2));
}

Simple C++03 Demo

Live On Coliru

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>
#include <iostream>

typedef int result_t;
struct A { int i; };
struct B : A { int j; };

typedef boost::shared_ptr<A> APtr;
typedef boost::shared_ptr<B> BPtr;

result_t f1(APtr) { return 1; }
result_t f2(BPtr) { return 2; }

typedef boost::function <result_t(APtr const&)> funOfA;
typedef boost::function <result_t(BPtr const&)> funOfB;

struct Wrapper {
typedef result_t result_type;
funOfB _f;

result_t operator()(APtr const& a) {
return _f(boost::static_pointer_cast<B>(a));
}
};

funOfA wrapFunOfB(const funOfB f) {
Wrapper wrap = { f };
return wrap;
}

void F(const funOfA f) {
APtr a = boost::make_shared<A>();
APtr b = boost::make_shared<B>();

//std::cout << "f(a): " << f(a) << "\n"; // UNDEFINED BEHAVIOUR if f wraps a funOfB
std::cout << "f(b): " << f(b) << "\n";
}

int main() {
F(f1);
F(wrapFunOfB(f2));
}

Prints

f(b): 1
f(b): 2

PROBLEMS, WARNINGS: dynamic_pointer_cast<>

If F actually invokes the parameter on an object that isn't actually of type B, that static_cast<> will invoke Undefined Behaviour.

If you want to protect against that, use dynamic_pointer_cast, which requires the classes A and B to be polymorphic types.

Live On Coliru

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>
#include <iostream>

typedef int result_t;
struct A { int i; virtual ~A() {} };
struct B : A { int j; };

typedef boost::shared_ptr<A> APtr;
typedef boost::shared_ptr<B> BPtr;

result_t f1(APtr a) { return a?1 : 0; }
result_t f2(BPtr b) { return b?2 : -99; }

typedef boost::function <result_t(APtr const&)> funOfA;
typedef boost::function <result_t(BPtr const&)> funOfB;

struct Wrapper {
typedef result_t result_type;
funOfB _f;

result_t operator()(APtr const& a) {
return _f(boost::dynamic_pointer_cast<B>(a));
}
};

funOfA wrapFunOfB(const funOfB f) {
Wrapper wrap = { f };
return wrap;
}

void F(const funOfA f) {
APtr a = boost::make_shared<A>();
APtr b = boost::make_shared<B>();

std::cout << "f(a): " << f(a) << "\n";
std::cout << "f(b): " << f(b) << "\n";
}

int main() {
F(f1);
F(wrapFunOfB(f2));
}

Prints

f(a): 1
f(b): 1
f(a): -99
f(b): 2

C++11 Version

Things get a little more elegant here. Notably, the Wrapper class can be local, and anonymous:

funOfA wrapFunOfB(const funOfB f) {
struct {
typedef result_t result_type;
funOfB _f;

result_t operator()(APtr const& a) {
return _f(std::dynamic_pointer_cast<B>(a));
}
} wrap { f };
return wrap;
}

Next level: use a lambda instead:

funOfA wrapFunOfB(const funOfB f) {
return [f](APtr const& a) { return f(std::dynamic_pointer_cast<B>(a)); };
}

Live On Coliru

#include <memory>
#include <functional>
#include <iostream>

typedef int result_t;
struct A { int i; virtual ~A() {} };
struct B : A { int j; };

typedef std::shared_ptr<A> APtr;
typedef std::shared_ptr<B> BPtr;

result_t f1(APtr a) { return a?1 : 0; }
result_t f2(BPtr b) { return b?2 : -99; }

typedef std::function<result_t(APtr const&)> funOfA;
typedef std::function<result_t(BPtr const&)> funOfB;

funOfA wrapFunOfB(const funOfB f) {
return [f](APtr const& a) { return f(std::dynamic_pointer_cast<B>(a)); };
}

void F(const funOfA f) {
APtr a = std::make_shared<A>();
APtr b = std::make_shared<B>();

std::cout << "f(a): " << f(a) << "\n";
std::cout << "f(b): " << f(b) << "\n";
}

int main() {
F(f1);
F(wrapFunOfB(f2));
}

That's 25% code reduction.

Cohabitation of boost::shared_ptr and std::shared_ptr

You could do it like this:

template<typename T>
boost::shared_ptr<T> make_shared_ptr(std::shared_ptr<T>& ptr)
{
return boost::shared_ptr<T>(ptr.get(), [ptr](T*) mutable {ptr.reset();});
}

template<typename T>
std::shared_ptr<T> make_shared_ptr(boost::shared_ptr<T>& ptr)
{
return std::shared_ptr<T>(ptr.get(), [ptr](T*) mutable {ptr.reset();});
}

EDIT: Note that this does not work with weak references to the source ptr. So be careful with those!

Should I switch from using boost::shared_ptr to std::shared_ptr?

There are a couple of reasons to switch over to std::shared_ptr:

  • You remove a dependency on Boost.
  • Debuggers. Depending on your compiler and debugger, the debugger may be "smart" about std::shared_ptr and show the pointed to object directly, where it wouldn't for say, boost's implementation. At least in Visual Studio, std::shared_ptr looks like a plain pointer in the debugger, while boost::shared_ptr exposes a bunch of boost's innards.
  • Other new features defined in your linked question.
  • You get an implementation which is almost guaranteed to be move-semantics enabled, which might save a few refcount modifications. (Theoretically -- I've not tested this myself) As of 2014-07-22 at least, boost::shared_ptr understands move semantics.
  • std::shared_ptr correctly uses delete [] on array types, while boost::shared_ptr causes undefined behavior in such cases (you must use shared_array or a custom deleter) (Actually I stand corrected. See this -- the specialization for this is for unique_ptr only, not shared_ptr.)

And one major glaring reason not to:

  • You'd be limiting yourself to C++11 compiler and standard library implementations.

Finally, you don't really have to choose. (And if you're targeting a specific compiler series (e.g. MSVC and GCC), you could easily extend this to use std::tr1::shared_ptr when available. Unfortunately there doesn't seem to be a standard way to detect TR1 support)

#if __cplusplus > 199711L
#include <memory>
namespace MyProject
{
using std::shared_ptr;
}
#else
#include <boost/shared_ptr.hpp>
namespace MyProject
{
using boost::shared_ptr;
}
#endif


Related Topics



Leave a reply



Submit