[SmartPtr] alternative to enable_shared_from_this
Dear Boost Community, I've been following this list for a time with the desire of contributing to Boost. It seems to me a good idea to start with small and easy to review proposals, before attempting anything big. So my first shot is shared_handle class, an alternative to enable_shared_from_this that doesn't suffer from multiple inheritance issues. It also provides the capability of instantiating a shared_ptr to a member object. doc: https://robhz786.github.io/smart_ptr/shared_handle.html source: https://github.com/boostorg/smart_ptr/compare/develop... robhz786:develop Unfortunately, the implementation produces at least one regression: clang 3.1 crashes when it compiles cpp11_pointer_cast_test.cpp with -std=c++11. There may be others since I couldn't test in all compilers used in the regression tests. Nevertheless, is there any interest? Kind regards, Roberto Hinz
On 3 January 2017 at 23:39, Roberto Hinz
Unfortunately, the implementation produces at least one regression: clang 3.1 crashes when it compiles cpp11_pointer_cast_test.cpp with -std=c++11.
Obviously this is a show-stopper! I presume shared_handle does compile with clang-1.0? What about VC-6 (the bit I'm interested in, as I'm running Win95 on my refrigerator)? Time for boost 2.0 ;-). degski
On 01/04/17 16:06, degski wrote:
On 3 January 2017 at 23:39, Roberto Hinz
wrote: Unfortunately, the implementation produces at least one regression: clang 3.1 crashes when it compiles cpp11_pointer_cast_test.cpp with -std=c++11.
Obviously this is a show-stopper! I presume shared_handle does compile with clang-1.0? What about VC-6 (the bit I'm interested in, as I'm running Win95 on my refrigerator)?
Not sure if you're serious, but not supporting ancient compilers in new components has never been a showstopper. Your refrigerator can keep using whatever Boost parts it is using now.
-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of degski Sent: 04 January 2017 13:07 To: boost Subject: Re: [boost] [SmartPtr] alternative to enable_shared_from_this
On 3 January 2017 at 23:39, Roberto Hinz
wrote: Unfortunately, the implementation produces at least one regression: clang 3.1 crashes when it compiles cpp11_pointer_cast_test.cpp with -std=c++11.
Obviously this is a show-stopper! I presume shared_handle does compile with clang-1.0? What about VC-6 (the bit I'm interested in, as I'm running Win95 on my refrigerator)?
Time for boost 2.0 ;-).
No need - Boost is into 3i - Incessant Incremental Improvement. Provided it will work with most current compilers, and you tell us which, that's fine. Paul --- Paul A. Bristow Prizet Farmhouse Kendal UK LA8 8AB +44 (0) 1539 561830
On 1/3/2017 4:39 PM, Roberto Hinz wrote:
Dear Boost Community,
I've been following this list for a time with the desire of contributing to Boost. It seems to me a good idea to start with small and easy to review proposals, before attempting anything big.
So my first shot is shared_handle class, an alternative to enable_shared_from_this that doesn't suffer from multiple inheritance issues. It also provides the capability of instantiating a shared_ptr to a member object.
doc: https://robhz786.github.io/smart_ptr/shared_handle.html source: https://github.com/boostorg/smart_ptr/compare/develop... robhz786:develop
Unfortunately, the implementation produces at least one regression: clang 3.1 crashes when it compiles cpp11_pointer_cast_test.cpp with -std=c++11. There may be others since I couldn't test in all compilers used in the regression tests.
Nevertheless, is there any interest?
clang 3.1 is ancient. I would not worry about it. Your code does not have to work with ancient compilers which no one uses anymore.
Roberto Hinz wrote:
Dear Boost Community,
I've been following this list for a time with the desire of contributing to Boost. It seems to me a good idea to start with small and easy to review proposals, before attempting anything big.
So my first shot is shared_handle class, an alternative to enable_shared_from_this that doesn't suffer from multiple inheritance issues. It also provides the capability of instantiating a shared_ptr to a member object.
doc: https://robhz786.github.io/smart_ptr/shared_handle.html
We already have something very similar, enable_shared_from_raw. It's still a base class, but since it's not templated, multiple inheritance scenarios can use virtual inheritance from enable_shared_from_raw.
On Wed, Jan 4, 2017 at 1:41 PM, Peter Dimov
We already have something very similar, enable_shared_from_raw. It's still a base class, but since it's not templated, multiple inheritance scenarios can use virtual inheritance from enable_shared_from_raw.
hmm, I didn't know enable_shared_from_raw until now. Still, can shared_handle be an alternative to enable_shared_from_raw ? enable_shared_from_raw has been criticized of been too prone to cause memory leaks [1] . shared_handle doesn't have this flaw. And although shared_handle still allow the user to shoot in his foot, the documentation makes it clear how to avoid the risks. An even if the user doesn't read it, the ugly function name shared_handle::forcibly_assemble_shared_ptr will encourage him to be careful. So I seems a safer version of enable_shared_from_raw. [1] http://stackoverflow.com/questions/22184772/why-is-boostenable-shared-from-r...
On 6/01/2017 06:50, Roberto Hinz wrote:
hmm, I didn't know enable_shared_from_raw until now. Still, can shared_handle be an alternative to enable_shared_from_raw ?
enable_shared_from_raw has been criticized of been too prone to cause memory leaks [1] .
[1] http://stackoverflow.com/questions/22184772/why-is-boostenable-shared-from-r...
The point there is that if you inherit enable_shared_from_raw and then provide some way to obtain a non-shared pointer to the object (ie. you don't use something like the shared factory pattern to guarantee that the object can't be constructed without a shared_ptr) then it will leak memory if you do so. (In much the same way that setting up a pointer cycle will leak memory, except on a smaller scale.) Conversely, if you inherit enable_shared_from_this and similarly provide some way to obtain a non-shared pointer to the object, then calling any method that assumes that there is a shared pointer (by calling shared_from_this()) will throw bad_weak_ptr. [Or in some older versions, is undefined behaviour instead.] Both of them result from misusing the types and both [now] have well-defined misbehaviours that result from doing so. You get to decide which misbehaviour you prefer. But both are also easily avoided by using factories.
On 2017-01-03 11:39 PM, Roberto Hinz wrote:
Dear Boost Community,
I've been following this list for a time with the desire of contributing to Boost. It seems to me a good idea to start with small and easy to review proposals, before attempting anything big.
So my first shot is shared_handle class, an alternative to enable_shared_from_this that doesn't suffer from multiple inheritance issues. It also provides the capability of instantiating a shared_ptr to a member object.
doc: https://robhz786.github.io/smart_ptr/shared_handle.html source: https://github.com/boostorg/smart_ptr/compare/develop... robhz786:develop
Unfortunately, the implementation produces at least one regression: clang 3.1 crashes when it compiles cpp11_pointer_cast_test.cpp with -std=c++11. There may be others since I couldn't test in all compilers used in the regression tests.
Nevertheless, is there any interest?
Kind regards, Roberto Hinz
Here is how I fixed multiple inheritance problem:
template <typename T>
class virtual_shared_from_this : public virtual
std::enable_shared_from_this<void>
{
public:
std::shared_ptr<T> shared_from_this()
{
return
std::shared_ptr<T>(std::enable_shared_from_this<void>::shared_from_this(),
static_cast
On Thu, Jan 5, 2017 at 1:13 PM, Boris Rasin
Here is how I fixed multiple inheritance problem:
template <typename T> class virtual_shared_from_this : public virtual std::enable_shared_from_this<void> { public: std::shared_ptr<T> shared_from_this() { return std::shared_ptr<T>(std::enable_shared_from_this<void>::shared_from_this(), static_cast
(this)); } }; You use virtual_shared_from_this exactly as you would use enable_shared_from_this. It supports multiple inheritance and is very efficient (no use of dynamic_cast).
This indeed solves the problem of multiple inheritance. But I think I
didn't expose well enough the other problem, which has to do with cyclic
references:
Suppose an object A has a shared_ptr to an object B which has a weak_ptr to
A. Every time this weak_ptr is used, it may not be eligible anymore to
originate a non null shared_ptr. In this case, if B can nicely handle this
locally ( checking the result of weak_ptr
El ene. 5, 2017, a las 10:02, Roberto Hinz
escribió: On Thu, Jan 5, 2017 at 1:13 PM, Boris Rasin
mailto:boris@pointx.org> wrote: Here is how I fixed multiple inheritance problem:
template <typename T> class virtual_shared_from_this : public virtual std::enable_shared_from_this<void> { public: std::shared_ptr<T> shared_from_this() { return std::shared_ptr<T>(std::enable_shared_from_this<void>::shared_from_this(), static_cast
(this)); } }; You use virtual_shared_from_this exactly as you would use enable_shared_from_this. It supports multiple inheritance and is very efficient (no use of dynamic_cast).
This indeed solves the problem of multiple inheritance. But I think I didn't expose well enough the other problem, which has to do with cyclic references:
Suppose an object A has a shared_ptr to an object B which has a weak_ptr to A. Every time this weak_ptr is used, it may not be eligible anymore to originate a non null shared_ptr. In this case, if B can nicely handle this locally ( checking the result of weak_ptr
::lock() ), then fine. But if it can't, is has to throw an exception. And this can be highly undesirable. One way to solve this problem would turn B a member object of A, or manage B with a unique_ptr owned by A. This way we ensure that A is never destroyed before B. But suppose that for some reason, there is something that requires a shared_ptr to B. How do you solve that?
For this exact scenario, you can use the aliasing constructor of shared_ptr (for reference, see constructor #8 on http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr): class A : public std::enable_shared_from_this<A> { // ... protected: std::shared_ptr<B> make_B_handle() const { return std::shared_ptr<B>{ shared_from_this(), b_.get() }; } private: std::unique_ptr<B> b_; }; The aliasing constructor says roughly that "I know that the pointer I'm giving you (the B* in this case) is valid if and only if the shared_ptr<A> is valid". You could even have the B directly in the A instead of via a unique_ptr (and then you give &b_ instead of b_.get()).
I have to agree that this is a very specific situation. But when it happens, it seems that it can only be solved with shared_handle ( or with enable_shared_from_raw which has no doc and I didn't know exists until now ).
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost http://lists.boost.org/mailman/listinfo.cgi/boost
On Thu, Jan 5, 2017 at 11:54 PM, Jared Grubb
El ene. 5, 2017, a las 10:02, Roberto Hinz
escribió: Suppose an object A has a shared_ptr to an object B which has a weak_ptr to A. Every time this weak_ptr is used, it may not be eligible anymore to originate a non null shared_ptr. In this case, if B can nicely handle this locally ( checking the result of weak_ptr
::lock() ), then fine. But if it can't, is has to throw an exception. And this can be highly undesirable. One way to solve this problem would turn B a member object of A, or manage B with a unique_ptr owned by A. This way we ensure that A is never destroyed before B. But suppose that for some reason, there is something that requires a shared_ptr to B. How do you solve that? For this exact scenario, you can use the aliasing constructor of shared_ptr (for reference, see constructor #8 on http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr < http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr>):
class A : public std::enable_shared_from_this<A> { // ... protected: std::shared_ptr<B> make_B_handle() const { return std::shared_ptr<B>{ shared_from_this(), b_.get() }; } private: std::unique_ptr<B> b_; };
The aliasing constructor says roughly that "I know that the pointer I'm giving you (the B* in this case) is valid if and only if the shared_ptr<A> is valid". You could even have the B directly in the A instead of via a unique_ptr (and then you give &b_ instead of b_.get()).
Well, that's a bit embarrassing, but I have to admit my ignorance now. If I knew this aliasing constructor of shared_ptr before, I wouldn't have elaborated shared_handle. It seems that the only remaining benefit of shared_ptr is that it can be used to impose the use make_shared or allocate_shared, which in turn ensures that a shared_ptr can aways be instantiated, including in the constructor. But this doesn't seem appealing enough. So lets drop the idea for now. Thank you all for the replies, Best regards Roberto
participants (9)
-
Andrey Semashev
-
Boris Rasin
-
degski
-
Edward Diener
-
Gavin Lambert
-
Jared Grubb
-
Paul A. Bristow
-
Peter Dimov
-
Roberto Hinz