Hi, I implemented std::unique_ptr using C++03 features plus a few other Boost Libraries. Incidentally, I also implemented default_delete (which binds to std::default_delete if available) because it's required for unique_ptr. https://github.com/helloworld922/unique_ptr The library binds to std::unique_ptr if standard library support is available. There are a few things I don't think I'll be able to emulate exactly. Namely: - I don't think the deleter can ever be forwarded correctly (limitation of C++03). Ideas/suggestions for how to best handle this are welcome (currently, I move the deleter if E == D and copy otherwise, I think) - There's no explicit conversion overloads in C++03. Currently I have operator bool() implemented non-explicitly if there's no compiler support, relying on the user to only use it "as-if explicit". An alternative solution is to remove the typecast operator and force users to use get() == NULL_VALUE. - functions relying on nullptr_t are only enabled if compiler support is available. Again, I don't think there's any possible work-around for C++03. There are also a few missing features (still working on the implementation): - make_unique (part of c++14) - nullptr_t comparison operators Current dependencies (assuming no C++11 std::unique_ptr): - Boost.Config - Boost.Move - Boost.TypeTraits Comments/Feedback is welcome.
On 08/03/2013 11:00 AM, Andrew Ho wrote:
I implemented std::unique_ptr using C++03 features plus a few other Boost Libraries. Incidentally, I also implemented default_delete (which binds to std::default_delete if available) because it's required for unique_ptr.
I would be very interested in seeing a C++03 based unique_ptr as part of Boost.SmartPtr Back in March there was some discussion about moving the unique_ptr implementation of Boost.Interprocess to Boost.SmartPtr. What happened to that proposal?
- There's no explicit conversion overloads in C++03. Currently I have operator bool() implemented non-explicitly if there's no compiler support, relying on the user to only use it "as-if explicit". An alternative solution is to remove the typecast operator and force users to use get() == NULL_VALUE.
Maybe you could use the safe-bool idiom. PS: I recommend that you use your real name in the copyright line rather than your handle. I do not think that the latter is legally valid.
I would be very interested in seeing a C++03 based unique_ptr as part of Boost.SmartPtr
Back in March there was some discussion about moving the unique_ptr implementation of Boost.Interprocess to Boost.SmartPtr. What happened to that proposal?
I actually didn't know Boost.Interprocess existed before. The interface looks very similar to standard c++11 unique_ptr, and if it matches the standard implementation as closely as possible, it would probably be a better fit because the implementation is likely to be less buggy.
Maybe you could use the safe-bool idiom.
Ah, thanks. Updated to use the safe-bool idiom.
PS: I recommend that you use your real name in the copyright line rather than your handle. I do not think that the latter is legally valid.
Fair enough. Updated copywrite info.
Does anyone know of nullptr emulated type already in Boost (similar to
http://stackoverflow.com/a/8747659/558546)?
I think this would be the best way to allow this:
my_uptr = nullptr; // effectively my_uptr.reset();
if(my_uptr == nullptr) // technically valid, though not sure if there's
significant gains from verbose writing vs. if(my_uptr)
I did some testing trying to store my unique_ptr implementation into a
std::vector, and things did not go well using a C++03 compiler (standard
library templates obviously aren't move aware in C++03). I'm not sure
there's any way to fix this for C++03 without re-implementing the standard
library containers, which really isn't practical.
I think this implementation does work properly with array types.
// on p1 destruction, delete[] is called, as expected
boost::unique_ptr
On 3 August 2013 19:39, Andrew Ho wrote:
// compiles, but so does std::unique_ptr // both will produce undefined runtime behavior on destruction of held value // dunno if this is GCC-4.8.1 specific, or if technically allowed by the standard and is user error. boost::unique_ptr<int> p3(new int[2]);
It's user error.
On 8/3/2013 2:00 AM, Andrew Ho wrote:
Hi,
I implemented std::unique_ptr using C++03 features plus a few other Boost Libraries. Incidentally, I also implemented default_delete (which binds to std::default_delete if available) because it's required for unique_ptr.
I never participated much in the discussion of Boost.Move, so I don't
know if this emulation approach was ever discussed, but once upon a
time I developed my own technique to simulate move-only types in
C++03. I never did anything with this technique when I first
discovered it (about 10 years ago now), and indeed completely forgot
about it until now.
Not extensively tested. Might miss important corner cases ...
template<typename T>
struct unique_ptr;
template<typename T>
struct unique_ptr_ref
{
explicit unique_ptr_ref(T *p)
: p_(p)
{}
private:
friend struct unique_ptr<T>;
T *p_;
};
template<typename T>
struct unique_ptr
{
unique_ptr()
: p_(0)
{}
explicit unique_ptr(T *p)
: p_(p)
{}
unique_ptr(unique_ptr_ref<T> r)
: p_(r.p_)
{}
~unique_ptr()
{
delete p_;
}
operator unique_ptr_ref<T>()
{
unique_ptr_ref<T> r(p_);
p_ = 0;
return r;
}
T *operator->() const
{
return p_;
}
T &operator*() const
{
return *p_;
}
private:
unique_ptr(unique_ptr &); // param must be non-const ref
unique_ptr &operator=(unique_ptr const &);
T *p_;
};
template<typename T>
unique_ptr<T> make_unique()
{
return unique_ptr<T>(new T);
}
template<typename T>
unique_ptr<T> move(unique_ptr<T> &p)
{
return unique_ptr<T>(static_cast
On 8/3/2013 2:12 PM, Eric Niebler wrote:
On 8/3/2013 2:00 AM, Andrew Ho wrote:
Hi,
I implemented std::unique_ptr using C++03 features plus a few other Boost Libraries. Incidentally, I also implemented default_delete (which binds to std::default_delete if available) because it's required for unique_ptr.
I never participated much in the discussion of Boost.Move, so I don't know if this emulation approach was ever discussed, but once upon a time I developed my own technique to simulate move-only types in C++03. I never did anything with this technique when I first discovered it (about 10 years ago now), and indeed completely forgot about it until now.
Not extensively tested. Might miss important corner cases ...
<snip>
Sorry! Forgot move assignment:
template<typename T>
struct unique_ptr;
template<typename T>
struct unique_ptr_ref
{
explicit unique_ptr_ref(T *p)
: p_(p)
{}
private:
friend struct unique_ptr<T>;
T *p_;
};
template<typename T>
struct unique_ptr
{
unique_ptr()
: p_(0)
{}
explicit unique_ptr(T *p)
: p_(p)
{}
unique_ptr(unique_ptr_ref<T> r)
: p_(r.p_)
{}
unique_ptr &operator=(unique_ptr_ref<T> r)
{
delete p_;
p_ = r.p_;
return *this;
}
~unique_ptr()
{
delete p_;
}
operator unique_ptr_ref<T>()
{
unique_ptr_ref<T> r(p_);
p_ = 0;
return r;
}
T *operator->() const
{
return p_;
}
T &operator*() const
{
return *p_;
}
private:
unique_ptr(unique_ptr &); // param must be non-const ref
unique_ptr &operator=(unique_ptr &);
T *p_;
};
template<typename T>
unique_ptr<T> make_unique()
{
return unique_ptr<T>(new T);
}
template<typename T>
unique_ptr<T> move(unique_ptr<T> &p)
{
return unique_ptr<T>(static_cast
on Sat Aug 03 2013, Eric Niebler
Not extensively tested. Might miss important corner cases ...
FWIW, there are implementations out there that are likely to be more-tested, e.g. http://home.roadrunner.com/~hinnant/unique_ptr03.html -- Dave Abrahams
On 8/5/2013 7:57 AM, Dave Abrahams wrote:
on Sat Aug 03 2013, Eric Niebler
wrote: Not extensively tested. Might miss important corner cases ...
FWIW, there are implementations out there that are likely to be more-tested, e.g. http://home.roadrunner.com/~hinnant/unique_ptr03.html
Thanks for the link. It looks like Howard and I discovered the same trick. -- Eric Niebler Boost.org http://www.boost.org
On Aug 5, 2013, at 3:49 PM, Eric Niebler
On 8/5/2013 7:57 AM, Dave Abrahams wrote:
on Sat Aug 03 2013, Eric Niebler
wrote: Not extensively tested. Might miss important corner cases ...
FWIW, there are implementations out there that are likely to be more-tested, e.g. http://home.roadrunner.com/~hinnant/unique_ptr03.html
Thanks for the link. It looks like Howard and I discovered the same trick.
I haven't been closely monitoring this thread, though I did note its existence. Fwiw, I was never thrilled with my C++03 emulation of unique_ptr. I don't recall exactly what doesn't work. But I think I'm correct when I say that it does not succeed in completely emulating a C++11 unique_ptr. There may be clues as to what doesn't work here: http://home.roadrunner.com/~hinnant/unique_ptr.zip Howard
Why don't you old on it. I talk to my girlfriend and she should have sent the keys to you today. Maybe we can meet after you get the key and if it works for you maybe I could get my deposit back at the same time
Thanks
Karim
Sent from my iPhone
On Aug 5, 2013, at 4:00 PM, Howard Hinnant
On Aug 5, 2013, at 3:49 PM, Eric Niebler
wrote: On 8/5/2013 7:57 AM, Dave Abrahams wrote:
on Sat Aug 03 2013, Eric Niebler
wrote: Not extensively tested. Might miss important corner cases ...
FWIW, there are implementations out there that are likely to be more-tested, e.g. http://home.roadrunner.com/~hinnant/unique_ptr03.html
Thanks for the link. It looks like Howard and I discovered the same trick.
I haven't been closely monitoring this thread, though I did note its existence.
Fwiw, I was never thrilled with my C++03 emulation of unique_ptr. I don't recall exactly what doesn't work. But I think I'm correct when I say that it does not succeed in completely emulating a C++11 unique_ptr. There may be clues as to what doesn't work here: http://home.roadrunner.com/~hinnant/unique_ptr.zip
Howard
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 8/5/2013 1:00 PM, Howard Hinnant wrote:
Fwiw, I was never thrilled with my C++03 emulation of unique_ptr. I don't recall exactly what doesn't work. But I think I'm correct when I say that it does not succeed in completely emulating a C++11 unique_ptr. There may be clues as to what doesn't work here: http://home.roadrunner.com/~hinnant/unique_ptr.zip
That's an extensive test suite; thanks for sharing. If I had time, I'd figure out how to run it and look into the failures. That's sadly not the case. :-( -- Eric Niebler Boost.org http://www.boost.org
I haven't been closely monitoring this thread, though I did note its existence.
Fwiw, I was never thrilled with my C++03 emulation of unique_ptr. I don't recall exactly what doesn't work. But I think I'm correct when I say that it does not succeed in completely emulating a C++11 unique_ptr. There may be clues as to what doesn't work here: http://home.roadrunner.com/~hinnant/unique_ptr.zip
Howard
Hi, I took a look at your implementation and incorporated parts of it into
my implementation.
I wrote some compiler tests to see how well an implementation of unique_ptr
stood up to the standard's requirements.
The main issues I found with Howard's implementations are (from a
compilation success standpoint, I haven't tried compilation failure test
cases):
1. Incompatibility with Boost.Move, and possibly certain Boost type traits.
2. It seems like a few areas rely on reference collapsing, which isn't
available in C++03 (well, that's what GCC 4.8.1 is telling me. Dunno if his
use is non-standard compliant).
3. There are some places the compiler complains about ambiguous use of
forward. Seems to primarily be a problem if D is a reference.
Howard's implementation did bring something interesting up I noticed about
the standard on array unique_ptr:
There is no move constructor/assignment of the form:
// constructor form here, similar issue with move assignment
unique_ptr
On 08/08/2013 11:38 AM, Andrew Ho wrote:
Hi, I took a look at your implementation and incorporated parts of it into my implementation.
I wrote some compiler tests to see how well an implementation of unique_ptr stood up to the standard's requirements.
The main issues I found with Howard's implementations are (from a compilation success standpoint, I haven't tried compilation failure test cases):
1. Incompatibility with Boost.Move, and possibly certain Boost type traits. 2. It seems like a few areas rely on reference collapsing, which isn't available in C++03 (well, that's what GCC 4.8.1 is telling me. Dunno if his use is non-standard compliant). 3. There are some places the compiler complains about ambiguous use of forward. Seems to primarily be a problem if D is a reference.
Hi, I am the one who started the "unique_ptr in C++03" topic on this list back in March. Unfortunately, I didn't have the time to work on its conclusions (i.e. move / port Boost.Interprocess' unique_ptr to Boost.SmartPtr). AFAIK, the implementation of Boost.Interprocess' unique_ptr is based on Howard Hinnant's code (see the header of boost/interprocess/smart_ptr/unique_ptr.hpp). I can only say that I've been using Boost.Interprocess' unique_ptr in a production code for a few months without any problems (not including [1]). As for C++03 emulated unique_ptr and std containers - I don't think these could ever work together properly. Fortunately, we have containers from Boost.Container which are move-aware and should work with unique_ptr emulations without problems. [1] - https://svn.boost.org/trac/boost/ticket/7598 WBR, Adam Romanek
Hi,
I am the one who started the "unique_ptr in C++03" topic on this list back in March. Unfortunately, I didn't have the time to work on its conclusions (i.e. move / port Boost.Interprocess' unique_ptr to Boost.SmartPtr).
AFAIK, the implementation of Boost.Interprocess' unique_ptr is based on Howard Hinnant's code (see the header of boost/interprocess/smart_ptr/unique_ptr.hpp).
Hi, I tried running Boost.Interprocess's unique_ptr through my compilation test suite. The implementation seems to make use of Boost.Move and places custom typetraits not in the boost namespace, so it should be compatible with both libraries. Notable failures: 1. No default_delete (easy to fix) 2. unique_ptr must specify deleter type (easy to fix) 3. Similar to Howard's implementation, there are issues with having D as a reference (forming reference to reference, forward implementations don't handle this case correctly) 4. I noticed in the code specialization for array unique_ptr has been commented out (boost/interprocess/smart_ptr/unique_ptr.hpp). Why? I don't know if it's in an external header file, but otherwise handling of array types won't work per standard.
I can only say that I've been using Boost.Interprocess' unique_ptr in a production code for a few months without any problems (not including [1]). [1] - https://svn.boost.org/trac/boost/ticket/7598
That's good to know. [1] has been fixed. The biggest bugs I have found with Howard's and Interprocess's unique_ptr deal with having D as a reference, which seems to be a rather obscure use case, which looks like is a rather obscure use case, but should still be handled properly if we want to emulate std::unique_ptr as closely as possible. Actually, [1] brought out a bug in my code which I had originally tried to avoid because boost::forward doesn't handle references correctly (there are bits in my implementation which explicitly avoid the use of boost::forward because of this). Looks like I'll have to look into properly implementing this to properly support D == A&.
As for C++03 emulated unique_ptr and std containers - I don't think these could ever work together properly. Fortunately, we have containers from Boost.Container which are move-aware and should work with unique_ptr emulations without problems.
WBR, Adam Romanek
I expected as much. C++03 std containers require copy-constructible objects and unique_ptr requires non-copyable.
Hi, 4. I noticed in the code specialization for array unique_ptr has been
commented out (boost/interprocess/smart_ptr/unique_ptr.hpp). Why? I don't know if it's in an external header file, but otherwise handling of array types won't work per standard.
Also the commented out code works for me, but I'm basically only using the [] operator. For gcc 4.6 to compile its only missing the headers for if_ and enable_if and some corresponding namespace issues. To me it looks the main issue is to get the boost::interprocess way of integration right.
Markus Mathes
Also the commented out code works for me, but I'm basically only using the [] operator. For gcc 4.6 to compile its only missing the headers for if_ and enable_if and some corresponding namespace issues. To me it looks the main issue is to get the boost::interprocess way of integration right.
I'm a bit hesitant to upgrade boost::interprocess::unique_ptr to be C++11 compliant right now because it currently doesn't handle references correctly, and I don't know what parts of the implementation have been vetted against the standard text. As my implementation stands right now I have vetted it against the standard text, and there are very few limitations compiler-wise. It's also designed to use C++11 features if available, where-as I don't see similar features in Interprocess's or Howard's implementation. At this point it's significantly easier for me to borrow bits and pieces from Interprocess.unique_ptr or Howard's unique_ptr rather than start from these implementations and work towards standard compliance. For example, I'm working on implementing nullptr_t emulation support similar to how Interprocess does it (using a nat*).
hmm... that's odd that the mailing list wasn't accepting my posts for a while. Sorry for spamming the list. FYI, this list post is the one I wanted to keep.
Markus Mathes
Also the commented out code works for me, but I'm basically only using the [] operator. For gcc 4.6 to compile its only missing the headers for if_ and enable_if and some corresponding namespace issues. To me it looks the main issue is to get the boost::interprocess way of integration right.
At this point it is significantly easier for me to take bits and pieces from Interprocess unique_ptr or Howard's unique_ptr implementation than start from either of them and work towards standard compliance (mainly due to handling of references). I have vetted my implementation to the standard text to get as close to standard behavior as possible, and implemented the library to use C++11 features if available.
Also the commented out code works for me, but I'm basically only using the [] operator. For gcc 4.6 to compile its only missing the headers for if_ and enable_if and some corresponding namespace issues. To me it looks the main issue is to get the boost::interprocess way of integration right.
Markus Mathes
At this time it's significantly easier for me to incorporate bits from Interprocess unique_ptr or Howard's unique_ptr into my implementation than try to get either of these implementations to be standard compliant. I have vetted my implementation to the standard text and it works as expected in nearly all cases. The main outstanding detail missing in my implementation that is solved with Interprocess's implementation is emulating a nullptr_t (Interprocess uses a nat*). To get Interprocess updated to be standard compliant and use C++11 features if available, there are quite a lot of changes required (many minor, a few fairly large, I listed the ones I immediately found previously). I can't think of any good methods for getting around the remaining problems with standard compliance which no C++03 implementation I've seen solves: 1. No construction from std::auto_ptr&&. std::auto_ptr isn't marked as boost movable, so boost::move doesn't return a BOOST_RV_REF(auto_ptr). I've pretty much accepted this as a limitation of any C++03 implementation. 2. std::swap isn't move aware. This forces an extra requirement on deleter_type to be copy-assignable, or use ADL swap. Again, I don't see any way around this without introducing some non-standard mechanism for move- aware swap.
Hi,
Also the commented out code works for me, but I'm basically only using the [] operator. For gcc 4.6 to compile its only missing the headers for if_ and enable_if and some corresponding namespace issues. To me it looks the main issue is to get the boost::interprocess way of integration right.
Markus Mathes
At this time it's significantly easier for me to incorporate bits from Interprocess unique_ptr or Howard's unique_ptr into my implementation than try to get either of these implementations to be standard compliant. I have vetted my implementation to the standard text and it works as expected in nearly all cases. The main outstanding detail missing in my implementation that is solved with Interprocess's implementation is emulating a nullptr_t (Interprocess uses a nat*). It should be fairly straight forward to extract the code from Interprocess to get this functionality in my implementation. To get Interprocess updated to be standard compliant and use C++11 features if available, there are quite a lot of changes required (many minor, a few fairly large, I listed the ones I immediately found previously). I can't think of any good methods for getting around the remaining problems with standard compliance which no C++03 implementation I've seen solves: 1. No construction from std::auto_ptr&&. std::auto_ptr isn't marked as boost movable, so boost::move doesn't return a BOOST_RV_REF(auto_ptr). I've pretty much accepted this as a limitation of any C++03 implementation. 2. std::swap isn't move aware. This forces an extra requirement on deleter_type to be copy-assignable, or use ADL swap. Again, I don't see any way around this without introducing some non-standard mechanism for move- aware swap.
On 8 August 2013 10:38, Andrew Ho wrote:
Howard's implementation did bring something interesting up I noticed about the standard on array unique_ptr:
There is no move constructor/assignment of the form:
// constructor form here, similar issue with move assignment unique_ptr
(move(unique_ptr )) Assuming I'm interpreting the standard correctly, this technically is a compiler error even if U is implicitly convertible to D and D is not a reference (e.g. U == D&). This is not the case for single element unique_ptr. Single element unique ptr will copy-construct D if U is implicitly convertible and a reference.
I don't know if this was intentionally done by the standards committee, or if it was a minor oversight. Howard's implementation does not allow this, GCC 4.8.1 allows this.
Thoughts/comments on how we should handle this in boost? My current implementation follows GCC's implementation, but it is very easy to change.
I think this is probably because I implemented the proposed resolution of http://cplusplus.github.io/LWG/lwg-active.html#2118 for GCC
From: helloworld922@gmail.com Date: Sat, 3 Aug 2013 09:00:28 +0000
I implemented std::unique_ptr using C++03 features plus a few other Boost Libraries. Incidentally, I also implemented default_delete (which binds to std::default_delete if available) because it's required for unique_ptr.
https://github.com/helloworld922/unique_ptr
The library binds to std::unique_ptr if standard library support is available. [SNIP] Current dependencies (assuming no C++11 std::unique_ptr):
- Boost.Config - Boost.Move - Boost.TypeTraits
Comments/Feedback is welcome.
Looking at "unique_ptr/boost/default_delete.hpp" as of SHA 6d294095747525ddb3a7481e374642156f5825ec: * If you're in an environment where the workaround for C++11 smart pointers are in play, then you probably don't have sibling-constructor (delegating) calls either. * When pointers are used for array new-ing, then cross-version conversions probably shouldn't be done, since U IS-A T doesn't mean that array-of-U IS-A array-of-T. (The spacing won't work right if the sizes of U and T differ. Neither will deletes unless a properly set custom deleter is used.) Daryle W.
Looking at "unique_ptr/boost/default_delete.hpp" as of SHA 6d294095747525ddb3a7481e374642156f5825ec:
* If you're in an environment where the workaround for C++11 smart pointers are in play, then you probably don't have sibling-constructor (delegating) calls either.
Oops, I removed the delegated constructor calls and used a call to a function which may not exist if the type is not convertible. However, the compiler error message this generates is less intuitive than usually (which is saying something for template compiler messages :P).
* When pointers are used for array new-ing, then cross-version conversions
probably shouldn't be done,
since U IS-A T doesn't mean that array-of-U IS-A array-of-T. (The spacing won't work right if the sizes of U and T differ. Neither will deletes unless a properly set custom deleter is used.)
I made the change such that U[] must be convertible to T[], though I'm not entirely sure this is the best solution. Is there any case where U[] is convertible to T[] where U != T? If this is the case I think the templated "copy" constructor could be removed completely for the array partial specialization. Check SHA 3f66052d35ce80c8df0abeb4a9b265e555de8f7f for patched default_delete.hpp thanks for the comments/suggestions.
Oops, I removed the delegated constructor calls and used a call to a function which may not exist if the type is not convertible. However, the compiler error message this generates is less intuitive than usually (which is saying something for template compiler messages :P).
update: I switched to a default function parameter to an internal nat struct, this should make the compiler message easier to understand without forcing/allowing users to do something "non-standard". In my tests, I have come up to a problem: std::unique_ptr allows the user to "move-construct" from a std::auto_ptr. However, this class is not marked as to be movable with move emulation. There are two solutions I could think of: 1. Remove the constructor if there's no true rvalue refs. 2. "Move-construct" from a std::auto_ptr&. 1 disallows some behavior which is standard, and 2 allows more behavior than the standard allows. Which should we go with, or does anyone know of a better work-around? My personal vote is for 1 as the user could just as easily do: // still valid standard c++11 my_uptr(my_aptr.release()); With 2 there is no way to stop the user from doing something non-standard compliant: // standard c++11 requires std::move(my_aptr) my_uptr(my_aptr); I think this issue and support for std::nullptr_t are the only two remaining pieces which would allow this class to be used in a non-standard compliant manner.
participants (10)
-
Adam Romanek
-
Andrew Ho
-
Bjorn Reese
-
Daryle Walker
-
Dave Abrahams
-
Eric Niebler
-
Howard Hinnant
-
Jonathan Wakely
-
Karim Buhagiar
-
Markus Mathes