C++03 unique_ptr emulation

I've put up a new version of an C++03 emulated unique_ptr here: http://home.roadrunner.com/~hinnant/unique_ptr03.html This is a "boost-ized" emulation (uses boost tools and is in boost namespace), though I have reason to believe it may not work well on VC+ +05 unless the /Za option is used (I've tested only on gcc 4.0). I'm aware of Ion's interprocess version (http://www.boost.org/doc/libs/1_35_0/doc/html/interprocess/interprocess_smar... ), and thank Ion for his support over the years. This new version is not meant to replace Ion's version. It is simply my current best shot at emulating the unique_ptr behavior as specified in the C++0X CD1 draft. Booster's are welcome to take parts of this implementation and use them in places like interprocess/unique_ptr. Or to just use this unique_ptr directly. It consists of only one header: unique_ptr.hpp, and requires only boost headers, not boost cpp files. Basic documentation is included at the link. -Howard

Howard Hinnant wrote:
I've put up a new version of an C++03 emulated unique_ptr here:
http://home.roadrunner.com/~hinnant/unique_ptr03.html
This is a "boost-ized" emulation (uses boost tools and is in boost namespace), though I have reason to believe it may not work well on VC++05 unless the /Za option is used (I've tested only on gcc 4.0).
Great!
I'm aware of Ion's interprocess version (http://www.boost.org/doc/libs/1_35_0/doc/html/interprocess/interprocess_smar...), and thank Ion for his support over the years. This new version is not meant to replace Ion's version. It is simply my current best shot at emulating the unique_ptr behavior as specified in the C++0X CD1 draft. Booster's are welcome to take parts of this implementation and use them in places like interprocess/unique_ptr. Or to just use this unique_ptr directly. It consists of only one header: unique_ptr.hpp, and requires only boost headers, not boost cpp files.
I would be really glad if I could just drop Interprocess' own version, since your version also covers non-raw pointers. I've seen that move emulation is done using a conversion operator (I think Boost.Thread uses the same technique), is that mechanism the best way to achieve this? Interprocess' own move-semantics are based on Dave Abramams good old move library so we already have different solutions for the same problem. I'm aware also of Adobe's Move library but that library requires Regular types (copy constructible). It would be great if we could adopt your unique_ptr version and agree a general move emulation protocol so that we can make Boost libraries interoperable. Interprocess containers already support emplace and move semantics so we can have containers of unique_ptrs or boost::threads. Regards, Ion

On Jan 2, 2009, at 6:45 PM, Ion Gaztañaga wrote:
I've seen that move emulation is done using a conversion operator (I think Boost.Thread uses the same technique), is that mechanism the best way to achieve this?
It is my current best effort. :-\ It does not have the move-from- const bug that my previous effort had. And it does not require clients to be aware of the "rv" or "moved-from" type (which is a characteristic I really like). In this emulation I've made that type private to emphasize the encapsulation of it from clients wanting to move unique_ptr's.
Interprocess' own move-semantics are based on Dave Abramams good old move library so we already have different solutions for the same problem. I'm aware also of Adobe's Move library but that library requires Regular types (copy constructible).
<nod>
It would be great if we could adopt your unique_ptr version and agree a general move emulation protocol so that we can make Boost libraries interoperable.
Agreed. The thing I like about my current effort is that clients see either lvalue or rvalue unique_ptr's (or whatever class you're trying to move-enable) and nothing else. No moved-from wrappers. Downsides include the fact that move(unique_ptr) is a friend of unique_ptr - a tight coupling that I would rather not have there. In C++0X, move is a completely generic std-function, a characteristic not achieved by this emulation.
Interprocess containers already support emplace and move semantics so we can have containers of unique_ptrs or boost::threads.
Great! :-) -Howard

on Fri Jan 02 2009, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:
Agreed. The thing I like about my current effort is that clients see either lvalue or rvalue unique_ptr's (or whatever class you're trying to move-enable) and nothing else. No moved-from wrappers. Downsides include the fact that move(unique_ptr) is a friend of unique_ptr - a tight coupling that I would rather not have there. In C++0X, move is a completely generic std-function, a characteristic not achieved by this emulation.
Howard, do you have a test suite I can throw at this? I'd like to see if I can decouple that thing for you. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Jan 2, 2009, at 8:56 PM, David Abrahams wrote:
on Fri Jan 02 2009, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:
Agreed. The thing I like about my current effort is that clients see either lvalue or rvalue unique_ptr's (or whatever class you're trying to move- enable) and nothing else. No moved-from wrappers. Downsides include the fact that move(unique_ptr) is a friend of unique_ptr - a tight coupling that I would rather not have there. In C++0X, move is a completely generic std-function, a characteristic not achieved by this emulation.
Howard, do you have a test suite I can throw at this? I'd like to see if I can decouple that thing for you.
Although I've been running tests, I don't really yet have a neatly packaged test suite. Part of the problem is that many of the tests are "must fail" and I don't know of a portable/automated way to handle that kind of test. Has boost addressed "must fail" tests? -Howard

Howard Hinnant wrote:
Although I've been running tests, I don't really yet have a neatly packaged test suite. Part of the problem is that many of the tests are "must fail" and I don't know of a portable/automated way to handle that kind of test. Has boost addressed "must fail" tests?
Isn't that what compile-fail Jam instructions are for? Just remember to put each test case in its own file, or failures will be masked. Sebastian

On Jan 3, 2009, at 10:51 AM, Sebastian Redl wrote:
Howard Hinnant wrote:
Although I've been running tests, I don't really yet have a neatly packaged test suite. Part of the problem is that many of the tests are "must fail" and I don't know of a portable/automated way to handle that kind of test. Has boost addressed "must fail" tests?
Isn't that what compile-fail Jam instructions are for? Just remember to put each test case in its own file, or failures will be masked.
I've never used Jam before. I'll take a look. -Howard

Howard Hinnant wrote:
I've never used Jam before. I'll take a look.
It may be a good idea to take the documentation from the side http://beta.boost.org/doc/ instead of the side http://www.boost.org/doc/ There seem to be some subtle issues that sometimes prevent the proper updating of the documentation at http://www.boost.org/doc/ (track tickets for this have been filled multiple times). This has hit me when I tried to learn Boost.Build, because I downloaded the (extremely) outdated manual http://www.boost.org/doc/libs/1_37_0/tools/build/v2/doc/userman.pdf instead of the current one http://beta.boost.org/doc/libs/1_37_0/tools/build/v2/doc/userman.pdf and then subscribed to boost.build asking silly questions that would have been covered at length in the current version of the manual. A recent message from Steven Watanabe ("This has been available on the beta site since November http://beta.boost.org/doc/libs/1_37_0?view=categorized but hasn't been merged to the live site.") just reminded me of this, so I checked and found out that the problems are not resolved yet. I learned from the comments at the tickets that this is something more subtle/difficult than a simple merge, but as long as these issues are not resolved, it seems better to recommend http://beta.boost.org/doc instead of http://www.boost.org/doc. Regards, Thomas

On Jan 3, 2009, at 12:56 PM, Thomas Klimpel wrote:
Howard Hinnant wrote:
I've never used Jam before. I'll take a look.
It may be a good idea to take the documentation from the side
instead of the side
There seem to be some subtle issues that sometimes prevent the proper updating of the documentation at http://www.boost.org/doc/ (track tickets for this have been filled multiple times). This has hit me when I tried to learn Boost.Build, because I downloaded the (extremely) outdated manual
http://www.boost.org/doc/libs/1_37_0/tools/build/v2/doc/userman.pdf
instead of the current one
http://beta.boost.org/doc/libs/1_37_0/tools/build/v2/doc/userman.pdf
and then subscribed to boost.build asking silly questions that would have been covered at length in the current version of the manual.
A recent message from Steven Watanabe ("This has been available on the beta site since November http://beta.boost.org/doc/libs/1_37_0?view=categorized but hasn't been merged to the live site.") just reminded me of this, so I checked and found out that the problems are not resolved yet.
I learned from the comments at the tickets that this is something more subtle/difficult than a simple merge, but as long as these issues are not resolved, it seems better to recommend http://beta.boost.org/doc instead of http://www.boost.org/doc.
Thanks much Thomas. -Howard

Thomas Klimpel wrote:
This has hit me when I tried to learn Boost.Build, because I downloaded the (extremely) outdated manual
http://www.boost.org/doc/libs/1_37_0/tools/build/v2/doc/userman.pdf
instead of the current one
http://beta.boost.org/doc/libs/1_37_0/tools/build/v2/doc/userman.pdf
and then subscribed to boost.build asking silly questions that would have been covered at length in the current version of the manual.
Interestingly enough, the above references are both outdated, and both http://www.boost.org/doc/tools/build/doc/userman.pdf http://beta.boost.org/doc/tools/build/doc/userman.pdf seem to be up to date. I just tried reproducing my problems, but now both http://www.boost.org/doc and http://beta.boost.org/doc seem to point me to the correct versions (I can't remember what I did differently some seconds before). Now I'm confused. Perhaps it's really just a simple merge issue?
Thanks much Thomas.
Sorry for the confusion. Regards, Thomas

On Sat, Jan 3, 2009 at 11:08 AM, Howard Hinnant <hinnant@twcny.rr.com> wrote:
On Jan 3, 2009, at 10:51 AM, Sebastian Redl wrote:
Howard Hinnant wrote:
Although I've been running tests, I don't really yet have a neatly packaged test suite. Part of the problem is that many of the tests are "must fail" and I don't know of a portable/automated way to handle that kind of test. Has boost addressed "must fail" tests?
Isn't that what compile-fail Jam instructions are for? Just remember to put each test case in its own file, or failures will be masked.
I've never used Jam before. I'll take a look.
It is a lot of work to gain even a moderately deep understanding of bjam, but if you treat it like magic, some of the simpler incantations are very easy to learn. compile-fail tests fall in the simple category. In https://svn.boost.org/svn/boost/sandbox/chrono, take a look at libs/chrono/test/Jamfile.v2 and ratio_fail_test1.cpp. If I run "bjam --toolset gcc", note that the test is reported as "**passed**", because there were compile errors. Temporarily change ratio_fail_test1.cpp so that the ratio multiply doesn't overflow, and the compile will work but the test will be reported as failing --Beman

On Jan 3, 2009, at 1:28 PM, Beman Dawes wrote:
On Sat, Jan 3, 2009 at 11:08 AM, Howard Hinnant <hinnant@twcny.rr.com> wrote:
On Jan 3, 2009, at 10:51 AM, Sebastian Redl wrote:
Howard Hinnant wrote:
Although I've been running tests, I don't really yet have a neatly packaged test suite. Part of the problem is that many of the tests are "must fail" and I don't know of a portable/automated way to handle that kind of test. Has boost addressed "must fail" tests?
Isn't that what compile-fail Jam instructions are for? Just remember to put each test case in its own file, or failures will be masked.
I've never used Jam before. I'll take a look.
It is a lot of work to gain even a moderately deep understanding of bjam, but if you treat it like magic, some of the simpler incantations are very easy to learn. compile-fail tests fall in the simple category.
In https://svn.boost.org/svn/boost/sandbox/chrono, take a look at libs/chrono/test/Jamfile.v2 and ratio_fail_test1.cpp.
If I run "bjam --toolset gcc", note that the test is reported as "**passed**", because there were compile errors.
Temporarily change ratio_fail_test1.cpp so that the ratio multiply doesn't overflow, and the compile will work but the test will be reported as failing
Forget pictures. A good example is truly worth a thousand words! :-) Thanks Beman. -Howard

On Jan 3, 2009, at 1:28 PM, Beman Dawes wrote:
It is a lot of work to gain even a moderately deep understanding of bjam, but if you treat it like magic, some of the simpler incantations are very easy to learn. compile-fail tests fall in the simple category.
In https://svn.boost.org/svn/boost/sandbox/chrono, take a look at libs/chrono/test/Jamfile.v2 and ratio_fail_test1.cpp.
If I run "bjam --toolset gcc", note that the test is reported as "**passed**", because there were compile errors.
Temporarily change ratio_fail_test1.cpp so that the ratio multiply doesn't overflow, and the compile will work but the test will be reported as failing
Despite the good and generous help, I'm afraid I'm running out of time to learn how to use bjam for testing compile time failures. Here's where I'm at: $ pwd <snip>/Development/unique_ptr $ printenv <snip> BOOST_ROOT=<snip>/Development/boost-dev/boost-trunk $ ls Jamfile.v2 unique_ptr.hpp unique_ptr_fail_test1.cpp $ cat Jamfile.v2 # Boost unique_ptr Library test Jamfile # Copyright Howard Hinnant 2009 # Distributed under the Boost Software License, Version 1.0. # See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt # See library home page at http://www.boost.org/libs/chrono project : requirements ; test-suite "unique_ptr" : [ compile-fail unique_ptr_fail_test1.cpp ] ; $ bjam --toolset gcc error: Could not find parent for project at '.' error: Did not find Jamfile.jam or Jamroot.jam in any parent directory. I'm afraid I don't know what a "parent for project" is. And while I could put an empty Jamfile.jam in a parent directory, I doubt that would help much. Fwiw, the starter docs I have at http://home.roadrunner.com/~hinnant/unique_ptr03.html have both positive and negative example code. I fear I don't have the cycles to really boost-ize this code. I hope that it is useful to some people anyway. -Howard

Despite the good and generous help, I'm afraid I'm running out of time to learn how to use bjam for testing compile time failures. Here's where I'm at: error: Could not find parent for project at '.' error: Did not find Jamfile.jam or Jamroot.jam in any parent directory.
I'm afraid I don't know what a "parent for project" is. And while I could put an empty Jamfile.jam in a parent directory, I doubt that would help much. Simply put, Jam thinks of projects as hierarchical. There's the root
Howard Hinnant wrote: project, which is basically "everything that concerns the application" and there's sub-projects, such as individual libraries, or individual tools in a tool suite. In Boost, the root project is Boost itself, while every library is a sub-project. Sub-projects can be further divided, e.g. into an executable and a test suite. The root project is controlled by a Jamroot file, while sub-projects use Jamfile files. If Jam finds a Jamfile, it walks the directory hierarchy upwards, collecting parent Jamfiles, until it hits the Jamroot - or the hierarchy root and errors out. The parent projects can introduce additional names, variables, requirements and dependencies. So basically, Jam is complaining because you used a Jamfile when your little project is already the root of all things. Anyway, I'll try to boostify your project and post back. Sebastian

On Jan 3, 2009, at 3:45 PM, Sebastian Redl wrote:
Despite the good and generous help, I'm afraid I'm running out of time to learn how to use bjam for testing compile time failures. Here's where I'm at: error: Could not find parent for project at '.' error: Did not find Jamfile.jam or Jamroot.jam in any parent directory.
I'm afraid I don't know what a "parent for project" is. And while I could put an empty Jamfile.jam in a parent directory, I doubt that would help much. Simply put, Jam thinks of projects as hierarchical. There's the root
Howard Hinnant wrote: project, which is basically "everything that concerns the application" and there's sub-projects, such as individual libraries, or individual tools in a tool suite. In Boost, the root project is Boost itself, while every library is a sub-project. Sub-projects can be further divided, e.g. into an executable and a test suite. The root project is controlled by a Jamroot file, while sub-projects use Jamfile files. If Jam finds a Jamfile, it walks the directory hierarchy upwards, collecting parent Jamfiles, until it hits the Jamroot - or the hierarchy root and errors out. The parent projects can introduce additional names, variables, requirements and dependencies.
So basically, Jam is complaining because you used a Jamfile when your little project is already the root of all things.
Anyway, I'll try to boostify your project and post back.
That is very kind of you. I've added some tests here: http://home.roadrunner.com/~hinnant/unique_ptr03.html I've written a simple bash shell script to handle must-fail tests. The testsuite isn't complete, but it is a decent start. It is fairly self-explanatory if you have a bash shell. Just: $ ./test pass (assuming permissions translate in the zip). One may have to: $ chmod 755 test first. Oh, and you'll have to change: INCLUDE="-I/Users/hinnant/Development/boost-dev/boost-trunk -I.." to point to your boost installation. -Howard

On Jan 3, 2009, at 3:45 PM, Sebastian Redl wrote:
Anyway, I'll try to boostify your project and post back.
That is very kind of you. I did something somewhat simpler. Attached is a patch against Boost
Howard Hinnant wrote: trunk that makes unique_ptr a part of the smart_ptr library, as far as the header and the tests are concerned. (Those are some of the examples, not the tests you just uploaded.) The patch does not modify smart_ptr.hpp, though. The tests are now executed as part of the normal test suite, and they all pass. They are somewhat incomplete, though. Sebastian

On Jan 3, 2009, at 4:45 PM, Sebastian Redl wrote:
On Jan 3, 2009, at 3:45 PM, Sebastian Redl wrote:
Anyway, I'll try to boostify your project and post back.
That is very kind of you. I did something somewhat simpler. Attached is a patch against Boost
Howard Hinnant wrote: trunk that makes unique_ptr a part of the smart_ptr library, as far as the header and the tests are concerned. (Those are some of the examples, not the tests you just uploaded.) The patch does not modify smart_ptr.hpp, though. The tests are now executed as part of the normal test suite, and they all pass. They are somewhat incomplete, though.
Thanks Sebastian. I'll continue to make incremental improvements to http://home.roadrunner.com/~hinnant/unique_ptr03.html , and hopefully the goal of simply exposing more C++ programmers to a preview of std::unique_ptr will be realized. -Howard

On Jan 3, 2009, at 6:41 PM, Howard Hinnant wrote:
On Jan 3, 2009, at 4:45 PM, Sebastian Redl wrote:
On Jan 3, 2009, at 3:45 PM, Sebastian Redl wrote:
Anyway, I'll try to boostify your project and post back.
That is very kind of you. I did something somewhat simpler. Attached is a patch against Boost
Howard Hinnant wrote: trunk that makes unique_ptr a part of the smart_ptr library, as far as the header and the tests are concerned. (Those are some of the examples, not the tests you just uploaded.) The patch does not modify smart_ptr.hpp, though. The tests are now executed as part of the normal test suite, and they all pass. They are somewhat incomplete, though.
Thanks Sebastian. I'll continue to make incremental improvements to http://home.roadrunner.com/~hinnant/unique_ptr03.html , and hopefully the goal of simply exposing more C++ programmers to a preview of std::unique_ptr will be realized.
I've uploaded what is I think a fairly complete unique_ptr.hpp and testsuite to: http://home.roadrunner.com/~hinnant/unique_ptr03.html If you download the zip, you get the header + testsuite. The testsuite is hierarchical, organized according to N2800's [unique.ptr]. One can: $ export CC=g++ $ export BOOST_INCLUDE="-I/<path-to>/boost-trunk" $ export SOURCE_INCLUDE="-I/<path-to-unique_ptr.hpp>" And then cd into any directory under unique.ptr and: $ ./test (assumes bash-compatible environment) This allows you to test as little or as much of the unique_ptr testsuite as you want, by cd'ing up or down and just testing what is under your pwd. I haven't put it into a boost sandbox. I have no objection if someone does (and adds-to/modifies it). I've included Ion on the copyright list as I felt I used some of his code. If you're not comfortable with that Ion, just let me know. If I've missed someone who should be on there, please let me know. I've done a poor job of keeping track of who's contributed what and would like to give credit where credit is due. If you want to add to the testsuite it is pretty self-explanatory. Just create a test and have the name end with ".pass.cpp" if the test is supposed to pass (return 0 on success). If the test is supposed to fail at compile time, name it ending with ".fail.cpp". Noteworthy points: * 3 of the tests currently fail for me (fail at compile time, supposed to compile, run and pass). These are all associated with the converting constructor specified in [unique.ptr.single.ctor]. When the source and target are of different type, this emulation demands that the conversion be explicit, and refuses to compile on implicit conversions: unique_ptr<base> b(unique_ptr<derived>()); // ok unique_ptr<base> b = unique_ptr<derived>(); // causes 3 compile time failures under unique.ptr/unique.ptr.single/unique.ptr.single.ctor I consider this not bad for an emulation. And otherwise things look pretty good. I was able to get more working than I thought I would. * I implemented my own boost::detail::is_convertible instead of using boost::is_convertible. My version does not handle function, array, void or abstract types, but does handle move-only types (this is all unique_ptr needs) by considering the From to be an rvalue. The implementation is a little squirrelly in that it is different depending upon whether From and To are the same type or not. I suspect, but do not know for sure, that this is related to CWG issue 291 (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#291) which has Dave Abraham's fingerprints on it (fwiw I am in favor of the resolution of this issue). A version which took into account array, function, void and abstract types could be built upon this (but probably wouldn't work for move-only abstract types unless rvalue references were supported by the compiler). * I implemented an abbreviated version of boost::compressed_pair (named detail::unique_ptr_storage) for similar reasons as above: The boost version lacked support for move-only types and unique_ptr needs it. I implemented as little as possible to get unique_ptr up and going. A move-aware compressed_pair (or space-optimizing, move-aware tuple) would be most interesting, but is not addressed herein. * There are namespace scope definitions for move and forward. They aren't perfect, but they aren't too bad. There's no doubt there's still a little room for improvement on forward (and a unique_ptr test to detect any such changes). * The move-only class author must know about boost::detail::rv (unique_ptr demonstrates use). The move-only class client needs to know only about boost::move and boost::forward. * A C++0X version of unique_ptr is not addressed here. Perhaps it will be in the future, I don't know right now. * No doubt others (and myself) will find good ways to improve both the implementation and testsuite. But this is the first version I'm willing to call fairly complete. I believe there are 151 tests: 63 that are supposed to pass and 88 that are supposed to fail. * This was all tested on g++ 4.0. I have no idea what it will take to get it working on other compilers. To the best of my knowledge, it should work on any conforming C++03 compiler. * This exercise generated one LWG issue against unique_ptr (which I haven't yet posted). As currently specified a unique_ptr<A[], Deleter> may convert to a unique_ptr<A, Deleter>. This implementation and testsuite has a test demanding this not happen, and I will get an LWG posted to this effect. -Howard

On Wed, 7 Jan 2009 20:40:13 -0500, Howard Hinnant <hinnant@twcny.rr.com> wrote:
Noteworthy points:
* 3 of the tests currently fail for me (fail at compile time, supposed to compile, run and pass). These are all associated with the converting constructor specified in [unique.ptr.single.ctor]. When the source and target are of different type, this emulation demands that the conversion be explicit, and refuses to compile on implicit conversions:
unique_ptr<base> b(unique_ptr<derived>()); // ok
unique_ptr<base> b = unique_ptr<derived>(); // causes 3 compile time failures under unique.ptr/unique.ptr.single/unique.ptr.single.ctor
I consider this not bad for an emulation. And otherwise things look pretty good. I was able to get more working than I thought I would.
This is consistent with the conversions I could not get to work in my Clang smart pointers. Sebastian

Hi, Howard Hinnant wrote:
* The move-only class author must know about boost::detail::rv (unique_ptr demonstrates use). The move-only class client needs to know only about boost::move and boost::forward.
Porting Interprocess to this move emulaton I have realized that a move function like template <class T> inline typename enable_if_c < !detail::is_convertible<T, detail::rv<T> >::value, T&
::type move(T& t) { return t; }
is quite problematic for container implementation. With the old emulation (that returned detail::rv<T>) this was possible: class vector { void push_back(const value_type &); void push_back(boost::detail::rv<value_type>); } //copy value_type v; v.push_back(v) //move value_type v; v.push_back(boost::move(v)); but this is not possible with this emulation. Moreover, for emplace functions, they could be implemented like this: template<class T1, class T2, class T3, ...> void emplace_back(const T1 &t1, const T2 &t2, const T3 &t3,....) The "rvalueness" was forwarded with detail::rv with some preprocessor tricks. Sadly, changing the return type from T to boost::detail::rv<value_type> works with Visual 7.1 but not with GCC 4.3: movable function(movable m) { return movable(boost::move(m)); } //this works both with Visual 7.1 and GCC 4.3 movable m; movable m2(boost::move(m); //this only works with visual movable m3(function(boost::move(m2)); With GCC 4.3 (surely more standard conforming that Visual 7.1) boost::detail::rv<> -> T conversion don't work when passing arguments in function calls. To solve this we need to explicitly call: movable m3(function(movable(boost::move(m2))); which is ugly. But not having a way to distinguish a pseudo-rvalue reference and a reference I think causes more harm than good. If we could somehow find a way to avoid writing "function(movable(boost::move(m2))" while maintaning boost::detail::rv<T> as the result of move() that would be perfect. Regards, Ion

On Jan 8, 2009, at 12:42 PM, Ion Gaztañaga wrote:
is quite problematic for container implementation. With the old emulation (that returned detail::rv<T>) this was possible:
class vector { void push_back(const value_type &); void push_back(boost::detail::rv<value_type>); }
Is it practical to do this instead? class vector { void push_back(value_type x); // internally move construct from x } You take a performance hit on lvalue copyable expressions which are expensive to move. But it gives you the functionality of: class vector { void push_back(const value_type&); void push_back(value_type&&); } And there is no hit on rvalue expressions (if I recall correctly, it has been a little while since I did this survey). I don't know, but from conversations with Sean, I suspect this is the direction the Adobe move emulation took. -Howard

2009/1/8 Howard Hinnant <hinnant@twcny.rr.com>:
Is it practical to do this instead?
class vector { void push_back(value_type x); // internally move construct from x }
I was doing this (which is inspired by either Adobe or David Abrahams): template <class U> void push_back(U const& x, typename boost::copy_sink<U, T>::type = 0) { if(data_.end_ == data_.storage_end_) resize(data_.begin_ ? size() * 2 : 4); std::uninitialized_fill_n(data_.end_, 1, x); ++data_.end_; } #if !defined(BOOST_NO_SFINAE) template <class U> void push_back(U x, typename boost::move_sink<U, T>::type = 0) { if(data_.end_ == data_.storage_end_) resize(data_.begin_ ? size() * 2 : 4); data_.end_ = boost::uninitialized_move(&x, &x + 1, data_.end_); } #endif This is taken from: http://svn.boost.org/svn/boost/sandbox/move/libs/move/test/vector.hpp The move header is at: http://svn.boost.org/svn/boost/sandbox/move/boost/move/move.hpp It is worth looking at (I can say that objectively as it's mostly based on other people's ideas). For emplace, you'd probably only want to do this for a small number of arguments. Daniel

on Thu Jan 08 2009, "Daniel James" <daniel_james-AT-fmail.co.uk> wrote:
2009/1/8 Howard Hinnant <hinnant@twcny.rr.com>:
Is it practical to do this instead?
class vector { void push_back(value_type x); // internally move construct from x }
I was doing this (which is inspired by either Adobe or David Abrahams):
template <class U> void push_back(U const& x, typename boost::copy_sink<U, T>::type = 0) { if(data_.end_ == data_.storage_end_) resize(data_.begin_ ? size() * 2 : 4); std::uninitialized_fill_n(data_.end_, 1, x); ++data_.end_; }
#if !defined(BOOST_NO_SFINAE)
template <class U> void push_back(U x, typename boost::move_sink<U, T>::type = 0) { if(data_.end_ == data_.storage_end_) resize(data_.begin_ ? size() * 2 : 4); data_.end_ = boost::uninitialized_move(&x, &x + 1, data_.end_); }
#endif
Those xxx_sink things are Adobe's. I never liked them, but they solve problems to which I've never found a better answer. I believe they are the current state of the art. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Howard Hinnant wrote:
On Jan 8, 2009, at 12:42 PM, Ion Gaztañaga wrote:
is quite problematic for container implementation. With the old emulation (that returned detail::rv<T>) this was possible:
class vector { void push_back(const value_type &); void push_back(boost::detail::rv<value_type>); }
Is it practical to do this instead?
class vector { void push_back(value_type x); // internally move construct from x }
I see that adobe vector has two overloads that are activated with enable_if-like expressions: template <typename U> void push_back(const U& x, typename copy_sink<U, T>::type = template <typename U> void push_back(U x, typename move_sink<U, T>::type = 0); Not that I totally dislike the solution but forwarding is a problem that has a more difficult solution since we can't avoid the overhead for non-movable types. For example: template<class T1, class T2, class T3,... iterator emplace_back(???, ???, ???, ...)? v.emplace_back(t1, boost::move(t2), t3, ...) The same happens when implementing forwarding functions, so I'm starting to think that putting T() in functions taking movable-only types by value is not a bad idea ;-) Regards, Ion

on Thu Jan 08 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
The same happens when implementing forwarding functions, so I'm starting to think that putting T() in functions taking movable-only types by value is not a bad idea ;-)
Sorry, but what do you mean by "putting T() in functions?" putting-milk-in-his-T'ly y'rs, -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
on Thu Jan 08 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
The same happens when implementing forwarding functions, so I'm starting to think that putting T() in functions taking movable-only types by value is not a bad idea ;-)
Sorry, but what do you mean by "putting T() in functions?"
putting-milk-in-his-T'ly y'rs,
void function (movable m); int main() { movable m; function(movable(boost::move(m))); function(movable()); return 0; } I mean that to pass movable-only objects per-value you need to put movable() in the argument. This is needed if boost::move returns boost::detail::rv<T> instead of T. Whether we return T or rv<T>, returning a movable type must types must construct a movable object in the return statement (see the attached test case and change the #define BOOST_MOVE_RETURN_OPTION define to play with both approaches): movable move_return_function () { if(cond){ return movable(); } else(cond){ movable m; return movable(boost::move(m)); } } This won't work in both cases: movable move_return_function () { movable m; return movable; } Returning T from move() avoids the need of explicitly specify movable() when passing arguments by value but makes forwarding really hard. On the other hand returning rv<T> makes forwarding easy but passing by value ugly. Of course, the question is if we can return something from move() that can be different from T so that forwarding is easy but has no need to specify movable() when passing by value. Regards, Ion

on Fri Jan 09 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
on Thu Jan 08 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
The same happens when implementing forwarding functions, so I'm starting to think
David Abrahams wrote: that
putting T() in functions taking movable-only types by value is not a bad idea ;-)
Sorry, but what do you mean by "putting T() in functions?"
putting-milk-in-his-T'ly y'rs,
void function (movable m);
int main() { movable m; function(movable(boost::move(m))); function(movable()); return 0; }
I mean that to pass movable-only objects per-value you need to put movable() in the argument.
do you mean, generically, "movable(...)"? I couldn't tell whether you think the default-ctor-ness of that has significance.
This is needed if boost::move returns boost::detail::rv<T> instead of T.
But boost::move should return T.
Whether we return T or rv<T>, returning a movable type must types must construct a movable object in the return statement (see the attached test case and change the #define BOOST_MOVE_RETURN_OPTION define to play with both approaches):
movable move_return_function () { if(cond){ return movable(); } else(cond){ movable m; return movable(boost::move(m)); } }
This won't work in both cases:
movable move_return_function () { movable m; return movable; }
If movable is a move-only type, no, it won't work. I'm not sure what point you're trying to make here.
Returning T from move() avoids the need of explicitly specify movable() when passing arguments by value but makes forwarding really hard.
I don't see how it impacts forwarding.
On the other hand returning rv<T> makes forwarding easy
I don't see that either. You'll deduce rv<T> in a generic forwarding signature instead of T, which has a lot of potential to screw thinigs up.
but passing by value ugly. Of course, the question is if we can return something from move() that can be different from T so that forwarding is easy but has no need to specify movable() when passing by value.
Still lost. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams <dave@boostpro.com> writes:
on Fri Jan 09 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
on Thu Jan 08 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
The same happens when implementing forwarding functions, so I'm starting to think
David Abrahams wrote: that
putting T() in functions taking movable-only types by value is not a bad idea ;-)
Sorry, but what do you mean by "putting T() in functions?"
putting-milk-in-his-T'ly y'rs,
void function (movable m);
int main() { movable m; function(movable(boost::move(m))); function(movable()); return 0; }
I mean that to pass movable-only objects per-value you need to put movable() in the argument.
do you mean, generically, "movable(...)"? I couldn't tell whether you think the default-ctor-ness of that has significance.
This is needed if boost::move returns boost::detail::rv<T> instead of T.
But boost::move should return T.
Sorry for jumping in here, but I'm not sure that it should. Consider movable m; boost::move(m); In C++0x, move(m) is equivalent to static_cast<movable&&>(m), which just obtains a reference. If boost::move<T> returns a T then the code above will create a temporary which moves the data out of m, and then destroy that temporary, leaving m a hollow shell. Anthony -- Anthony Williams Author of C++ Concurrency in Action | http://www.manning.com/williams Custom Software Development | http://www.justsoftwaresolutions.co.uk Just Software Solutions Ltd, Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK

on Fri Jan 09 2009, Anthony Williams <anthony.ajw-AT-gmail.com> wrote:
boost::move should return T.
Sorry for jumping in here, but I'm not sure that it should. Consider
movable m; boost::move(m);
In C++0x, move(m) is equivalent to static_cast<movable&&>(m), which just obtains a reference. If boost::move<T> returns a T then the code above will create a temporary which moves the data out of m, and then destroy that temporary, leaving m a hollow shell.
So you're saying, in other words, that "move(x)" really means "you have permission to move x" but the one I proposed would mean "move it, now." OK, good point. So what about this horrible little proposal? template <class T> struct rv<T> : T { private: rv(); ~rv(); rv(rv const&); void operator=(rv const&); }; template <class T> boost::enable_if<is_class<T>, rv<T>&> move(T& x) { return static_cast<rv<T>& >(x); } Does that solve any problems? Yeah, I know it's not theoretically portable, but it should be portable in practice. Especially when it comes to emulating language features, I care less and less about the letter of the law :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
So you're saying, in other words, that "move(x)" really means "you have permission to move x" but the one I proposed would mean "move it, now."
OK, good point.
So what about this horrible little proposal?
template <class T> struct rv<T> : T { private: rv(); ~rv(); rv(rv const&); void operator=(rv const&); };
template <class T> boost::enable_if<is_class<T>, rv<T>&> move(T& x) { return static_cast<rv<T>& >(x); }
Does that solve any problems?
How can that work? What would you need to add to T? Disable non-const copy constructor?
Yeah, I know it's not theoretically portable, but it should be portable in practice. Especially when it comes to emulating language features, I care less and less about the letter of the law :-)
I would be really interested in this proposal. Regards, Ion

on Fri Jan 09 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
David Abrahams wrote:
So you're saying, in other words, that "move(x)" really means "you have permission to move x" but the one I proposed would mean "move it, now."
OK, good point.
So what about this horrible little proposal?
template <class T> struct rv<T> : T { private: rv(); ~rv(); rv(rv const&); void operator=(rv const&); };
template <class T> boost::enable_if<is_class<T>, rv<T>&> move(T& x) { return static_cast<rv<T>& >(x); }
Does that solve any problems?
How can that work? What would you need to add to T? Disable non-const copy constructor?
Well, I haven't thought this through deeply (nor really tested anything), but the idea is that movable types would have a stealing conversion from rv<T>& but non-movable types should just use the copy ctor that slices off the non-existent derived class.
Yeah, I know it's not theoretically portable, but it should be portable in practice. Especially when it comes to emulating language features, I care less and less about the letter of the law :-)
I would be really interested in this proposal.
I'm not even sure if it addresses the problems you're working on, but it's a thought. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG David Abrahams wrote:
David Abrahams wrote:
So you're saying, in other words, that "move(x)" really means "you have permission to move x" but the one I proposed would mean "move it, now."
OK, good point.
So what about this horrible little proposal?
template <class T> struct rv<T> : T { private: rv(); ~rv(); rv(rv const&); void operator=(rv const&); };
template <class T> boost::enable_if<is_class<T>, rv<T>&> move(T& x) { return static_cast<rv<T>& >(x); }
Does that solve any problems?
Well, I haven't thought this through deeply (nor really tested anything), but the idea is that movable types would have a stealing conversion from rv<T>& but non-movable types should just use the copy ctor that slices off the non-existent derived class.
What if T has a fully generic constructor? In Christ, Steven Watanabe

on Fri Jan 09 2009, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
AMDG
David Abrahams wrote:
David Abrahams wrote:
So you're saying, in other words, that "move(x)" really means "you have permission to move x" but the one I proposed would mean "move it, now."
OK, good point.
So what about this horrible little proposal?
template <class T> struct rv<T> : T { private: rv(); ~rv(); rv(rv const&); void operator=(rv const&); };
template <class T> boost::enable_if<is_class<T>, rv<T>&> move(T& x) { return static_cast<rv<T>& >(x); }
Does that solve any problems?
Well, I haven't thought this through deeply (nor really tested anything), but the idea is that movable types would have a stealing conversion from rv<T>& but non-movable types should just use the copy ctor that slices off the non-existent derived class.
What if T has a fully generic constructor?
That's detectable, at least. For such types, you disable this move unless some trait is specialized. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: <boost@lists.boost.org> Sent: Friday, January 09, 2009 9:15 PM Subject: Re: [boost] C++03 unique_ptr emulation
David Abrahams wrote:
So you're saying, in other words, that "move(x)" really means "you have permission to move x" but the one I proposed would mean "move it, now."
OK, good point.
So what about this horrible little proposal?
template <class T> struct rv<T> : T { private: rv(); ~rv(); rv(rv const&); void operator=(rv const&); };
template <class T> boost::enable_if<is_class<T>, rv<T>&> move(T& x) { return static_cast<rv<T>& >(x); }
Does that solve any problems?
How can that work? What would you need to add to T? Disable non-const copy constructor?
Yeah, I know it's not theoretically portable, but it should be portable in practice. Especially when it comes to emulating language features, I care less and less about the letter of the law :-)
I would be really interested in this proposal.
Attaced it is your move_test adapted to the new proposal. It works on more cases than before. Define ALT1to come back to the preceding definition of move and rv. Thanks, Vicente

vicente.botet wrote:
----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: <boost@lists.boost.org> Sent: Friday, January 09, 2009 9:15 PM Subject: Re: [boost] C++03 unique_ptr emulation
Attaced it is your move_test adapted to the new proposal. It works on more cases than before. Define ALT1to come back to the preceding definition of move and rv.
I've corrected some errors when compiling with GCC 4.3 (some temporary "movable()" trying to bind to "movable &"). Thanks for the changes! This approach seems promising. Regards, Ion

----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: <boost@lists.boost.org> Sent: Saturday, January 10, 2009 11:19 AM Subject: Re: [boost] C++03 unique_ptr emulation
vicente.botet wrote:
----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: <boost@lists.boost.org> Sent: Friday, January 09, 2009 9:15 PM Subject: Re: [boost] C++03 unique_ptr emulation
Attaced it is your move_test adapted to the new proposal. It works on more cases than before. Define ALT1to come back to the preceding definition of move and rv.
I've corrected some errors when compiling with GCC 4.3 (some temporary "movable()" trying to bind to "movable &").
Could we know which changes? Can you share the new file?
Thanks for the changes! This approach seems promising.
I belive also. Thanks for pushing for a common Move library, Vicente

on Sat Jan 10 2009, Ion Gaztañaga <igaztanaga-AT-gmail.com> wrote:
vicente.botet wrote:
Could we know which changes? Can you share the new file?
Ooops! I thought a attached the test in the previous post, sorry. It's attached (I hope).
Regards,
I'm starting to sound like a broken record here. Why not just use the sandbox repo? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
This is needed if boost::move returns boost::detail::rv<T> instead of T.
But boost::move should return T.
Why? && is a reference, boost::move could return a pseudo-reference.
Returning T from move() avoids the need of explicitly specify movable() when passing arguments by value but makes forwarding really hard.
I don't see how it impacts forwarding.
How can you implement a "forwarding"-like function like this without penalyzing non-movable types? template<class T1, class T2, class T3, ...> emplace(...) if move() returns a pseudo-reference, this almost works easily (non-const references are forwarded as const-references but this might be acceptable for an emulation: template<class T1, class T2, ...> emplace(const T1 &t1, const T1 &t2, ...){ void *ptr = allocate(); new(ptr)(t1, t2, ....) }
On the other hand returning rv<T> makes forwarding easy
I don't see that either. You'll deduce rv<T> in a generic forwarding signature instead of T, which has a lot of potential to screw thinigs up.
See above.
Still lost.
I hope it's more clear now, Ion

on Thu Jan 08 2009, Howard Hinnant <hinnant-AT-twcny.rr.com> wrote:
On Jan 8, 2009, at 12:42 PM, Ion Gaztañaga wrote:
is quite problematic for container implementation. With the old emulation (that returned detail::rv<T>) this was possible:
class vector { void push_back(const value_type &); void push_back(boost::detail::rv<value_type>); }
Is it practical to do this instead?
class vector { void push_back(value_type x); // internally move construct from x }
You take a performance hit on lvalue copyable expressions which are expensive to move. But it gives you the functionality of:
class vector { void push_back(const value_type&); void push_back(value_type&&); }
And there is no hit on rvalue expressions (if I recall correctly, it has been a little while since I did this survey).
I don't know, but from conversations with Sean, I suspect this is the direction the Adobe move emulation took.
IIRC, that is the "first cut" direction it took, but you can do something uglier and more complex when you want to optimize the expensive-to-move lvalue copyable expressions. Take a look at their container implementations. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG Howard Hinnant wrote:
I haven't put it into a boost sandbox. I have no objection if someone does (and adds-to/modifies it). I've included Ion on the copyright list as I felt I used some of his code. If you're not comfortable with that Ion, just let me know. If I've missed someone who should be on there, please let me know. I've done a poor job of keeping track of who's contributed what and would like to give credit where credit is due.
I've added Jamfiles and put it at https://svn.boost.org/svn/boost/sandbox/unique_ptr In Christ, Steven Watanabe

AMDG Howard Hinnant wrote:
* No doubt others (and myself) will find good ways to improve both the implementation and testsuite. But this is the first version I'm willing to call fairly complete. I believe there are 151 tests: 63 that are supposed to pass and 88 that are supposed to fail.
* This was all tested on g++ 4.0. I have no idea what it will take to get it working on other compilers. To the best of my knowledge, it should work on any conforming C++03 compiler.
There are 9 failures on on msvc 9.0 Express and 17 on como. Output attached. In Christ, Steven Watanabe

On Jan 8, 2009, at 4:53 PM, Steven Watanabe wrote:
AMDG
Howard Hinnant wrote:
* No doubt others (and myself) will find good ways to improve both the implementation and testsuite. But this is the first version I'm willing to call fairly complete. I believe there are 151 tests: 63 that are supposed to pass and 88 that are supposed to fail.
* This was all tested on g++ 4.0. I have no idea what it will take to get it working on other compilers. To the best of my knowledge, it should work on any conforming C++03 compiler.
There are 9 failures on on msvc 9.0 Express and 17 on como. Output attached.
In Christ, Steven Watanabe
Thanks Steven, The first como failure (unique.ptr\unique.ptr.runtime \unique.ptr.runtime.ctor\default02.pass.cpp) looks like a test bug. B<D>::B() should be outlined next to ~B() instead of inlined. I started going through the rest of the como failures and they all look like a failure in detail::is_convertible. My best guess is that this version of como hasn't implemented CWG issue 291 (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#291 ), which is understandable considering this issue is C++0X targeted, not C++03 targeted (accepted Oct. 05). <sigh> is_convertible is a tricky area and apparently my implementation is depending on a dark corner that won't be official until C++0X. I do not know if changing "test1" to "test2" on line 50 of unique_ptr.hpp would help or not. It doesn't help on g++. It might help on Como. This is a dark corner of the language <shrug>. If it doesn't help, I don't have a backup plan. In this case it would appear that there is no C++03 technique for implementing is_convertible for move-only types (that I'm aware of). And if that is the case, a C++03 emulation of move seems very much in jeopardy. A working is_convertible is the foundation upon which this move emulation is built. -Howard

AMDG Howard Hinnant wrote:
The first como failure (unique.ptr\unique.ptr.runtime\unique.ptr.runtime.ctor\default02.pass.cpp) looks like a test bug. B<D>::B() should be outlined next to ~B() instead of inlined.
Confirmed. I made the change and it passes now.
I started going through the rest of the como failures and they all look like a failure in detail::is_convertible. My best guess is that this version of como hasn't implemented CWG issue 291 (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#291), which is understandable considering this issue is C++0X targeted, not C++03 targeted (accepted Oct. 05). <sigh> is_convertible is a tricky area and apparently my implementation is depending on a dark corner that won't be official until C++0X. I do not know if changing "test1" to "test2" on line 50 of unique_ptr.hpp would help or not. It doesn't help on g++. It might help on Como. This is a dark corner of the language <shrug>. If it doesn't help, I don't have a backup plan.
It doesn't help.
In this case it would appear that there is no C++03 technique for implementing is_convertible for move-only types (that I'm aware of). And if that is the case, a C++03 emulation of move seems very much in jeopardy. A working is_convertible is the foundation upon which this move emulation is built.
In Christ, Steven Watanabe

Howard Hinnant wrote:
On Jan 2, 2009, at 6:45 PM, Ion Gaztañaga wrote:
I've seen that move emulation is done using a conversion operator (I think Boost.Thread uses the same technique), is that mechanism the best way to achieve this?
It is my current best effort. :-\ It does not have the move-from-const bug that my previous effort had. And it does not require clients to be aware of the "rv" or "moved-from" type (which is a characteristic I really like). In this emulation I've made that type private to emphasize the encapsulation of it from clients wanting to move unique_ptr's.
Ok. For some vector and deque code I've used the is_movable<> trait to select move_iterator or just raw pointers for internal copies. Maybe this is still necessary with your conversion operator code. Although a smart move_iterator could do the job, using raw pointers in those cases automatically takes advantage of STL uninitialized_copy/copy functions that automatically use memcpy.
Agreed. The thing I like about my current effort is that clients see either lvalue or rvalue unique_ptr's (or whatever class you're trying to move-enable) and nothing else. No moved-from wrappers. Downsides include the fact that move(unique_ptr) is a friend of unique_ptr - a tight coupling that I would rather not have there. In C++0X, move is a completely generic std-function, a characteristic not achieved by this emulation.
Well, not a big problem if the emulation works! Regards, Ion
participants (10)
-
Anthony Williams
-
Beman Dawes
-
Daniel James
-
David Abrahams
-
Howard Hinnant
-
Ion Gaztañaga
-
Sebastian Redl
-
Steven Watanabe
-
Thomas Klimpel
-
vicente.botet