As you know it is not possible to use the std::enable_shared_from_this and shared_from_this() pair from the constructor of an object since a shared_pointer containing the class is not yet in existance. However, I really would like this functionality. I have attempted my own system and it seems to be working OK.
namespace kp
{
template <class T>
void construct_deleter(T *t)
{
if(!t->_construct_pself)
{
t->~T();
}
free(t);
}
template <class T, typename... Params>
std::shared_ptr<T> make_shared(Params&&... args)
{
std::shared_ptr<T> rtn;
T *t = (T *)calloc(1, sizeof(T));
t->_construct_pself = &rtn;
rtn.reset(t, construct_deleter<T>);
t = new(t) T(std::forward<Params>(args)...);
t->_construct_pself = NULL;
t->_construct_self = rtn;
return rtn;
}
template <class T>
class enable_shared_from_this
{
public:
std::shared_ptr<T> *_construct_pself;
std::weak_ptr<T> _construct_self;
std::shared_ptr<T> shared_from_this()
{
if(_construct_pself)
{
return *_construct_pself;
}
else
{
return _construct_self.lock();
}
}
};
}
Can anyone spot any flaws in this logic? I basically use placement new to assign a pointer to the shared_ptr inside the class before the constructor calls.
As it stands I can use it as so:
std::shared_ptr<Employee> emp = kp::make_shared<Employee>("Karsten", 30);
and in the Employee constructor:
Employee::Employee(std::string name, int age)
{
Dept::addEmployee(shared_from_this());
}
Before I commit this to a relatively large codebase, I would really appreciate some ideas or feedback from you guys.
Thanks!
I know it's been a while but that might be useful to someone with the same issue : the main problem will happen if you attempt to inherit from a class inheriting your enable_shared_from_this
.
Especially with this line :
t->_construct_pself = &rtn;
If you have let's say :
class Object : public kp::enable_shared_from_this<Object> {
};
class Component : public Object {
};
Then the compiler won't be able to cast std::shared_ptr<Component>*
to std::shared_ptr<Object>*
as for the compiler those types are not related even though Component
inherits Object
.
The easiest solution I see would be to turn _construct_pself
to void*
like so :
template <class T>
class enable_shared_from_this
{
public:
void* _construct_pself{ nullptr };
std::weak_ptr<T> _construct_self;
std::shared_ptr<T> shared_from_this() const
{
if (_construct_pself)
{
return *static_cast<std::shared_ptr<T>*>(_construct_pself);
}
else
{
return _construct_self.lock();
}
}
};
And then do
t->_construct_pself = static_cast<void*>(&rtn);
It's not very sexy and might make other issues arise but it seems to be working...
[EDIT] There is a slightly better and more "C++" alternative, sorry for not thinking about it right away, just do :
t->_construct_pself = reinterpret_cast<decltype(t->_construct_pself)>(&rtn);
[EDIT2] Make shared_from_this
const as it does not change anything in the class
[EDIT3] Found an other issue : If you use a copy constructor via make_shared
and use operator=
inside the constructor before shared_from_this
, shared_from_this
will return the address of copied object, not of the object's copy. Only solution I see is to define empty copy constructor and assignment operator for enable_shared_from_this
and explicitly call the copy constructor from inheriting classes everytime needed... Either that or MAKE SURE you NEVER call operator=
before shared_from_this
inside your copy constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With