|
Boost : |
Subject: Re: [boost] [move] new rvalue reference emulation
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2012-01-31 02:27:33
On Mon, Jan 23, 2012 at 10:16 AM, Vicente J. Botet Escriba <
vicente.botet_at_[hidden]> wrote:
> Le 23/01/12 17:55, Jeffrey Lee Hellrung, Jr. a écrit :
>
>> On Sat, Jan 21, 2012 at 4:22 PM, Vicente Botet<vicente.botet_at_[hidden]**
>> >wrote:
>>
>> Hi,
>>>
>>> Are there any news about this Move improvements?
>>>
>>> Best,
>>> Vicente
>>>
>>
>> None from my end.
>>
>> I've thought about it a bit since, and I *think* you can get by using this
>> with any change to the existing Boost.Move infrastructure (only
>> additions),
>> so, if desired, it could be added to an "advanced techniques in C++03"
>> section or something like that.
>>
>> Do you think it's worth adding to Boost.Move?
>>
>> I need to emulate the thread constructor
>
> template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
>
> at least for 2 args.
>
> If I have understood, there are some constraints when applying your
> technique on constructors, but guess that this is better than nothing.
>
> So yes, I would like to see a clear description on how to emulate this
> kind of functions with Boost.Move+ your additions
>
Apologies for the long delay. I finally got a minimal example program out
to illustrate the idea.
----------------
#include <iostream>
#include <typeinfo>
#include <boost/move/move.hpp>
#include <boost/mpl/always.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/utility/addressof.hpp>
#include <boost/utility/enable_if.hpp>
namespace
{
// Necessary to workaround an MSVC9 ICE.
template<
class Pred, class T,
bool = boost::has_move_emulation_enabled<T>::value
>
struct rv_sink_enable_ctor;
template< class Pred, class T>
struct rv_sink_enable_ctor< Pred, T, false >
{ };
template< class Pred, class T>
struct rv_sink_enable_ctor< Pred, T, true >
: boost::enable_if_c<
boost::mpl::apply1< Pred, T >::type::value
>
{ };
template<
class Visitor,
class Result = void,
class Pred = boost::mpl::always< boost::true_type >
>
struct rv_sink
{
typedef Result result_type;
// implicit on purpose
template< class T >
rv_sink(T const & x,
typename rv_sink_enable_ctor< Pred, T >::type* = 0)
: m_apply(apply<T>),
mp(static_cast< void* >(boost::addressof(const_cast< T& >(x))))
{ }
result_type operator()(Visitor visitor) const
{ return m_apply(visitor, mp); }
private:
template< class T >
static result_type apply(Visitor visitor, void* p)
{ return visitor(boost::move(*static_cast< T* >(p))); }
result_type (&m_apply)(Visitor, void*);
void* const mp;
};
struct A
{
private:
struct ctor_rv_sink
{
struct binder
{
explicit binder(A& this_) : m_this(this_) { }
typedef void result_type;
template< class T >
void operator()(T& x) const
{ return m_this.init(x); }
private:
A& m_this;
};
typedef rv_sink< binder > type;
};
public:
// lvalues
template< class T >
explicit A(T& x)
{ init(x); }
// movable rvalues
explicit A(ctor_rv_sink::type const x)
{ x(ctor_rv_sink::binder(*this)); }
// const lvalues + non-movable rvalues
template< class T >
explicit A(T const & x,
typename boost::disable_if_c<
boost::has_move_emulation_enabled<T>::value
>::type* = 0)
{ init(x); }
private:
template< class T >
void init(T&)
{
std::cout << "A::init(T&) with T = "
<< typeid( T ).name()
<< (boost::is_const<T>::value ? " (const)" : "")
<< std::endl;
}
};
struct X
{
X()
{ std::cout << "X::X()" << std::endl; }
X(X const &)
{ std::cout << "X::X(X const &)" << std::endl; }
X& operator=(X const &)
{
std::cout << "X::operator=(X const &)" << std::endl;
return *this;
}
static X make()
{ return X(); }
};
struct Y
{
BOOST_COPYABLE_AND_MOVABLE( Y )
public:
Y()
{ std::cout << "Y::Y()" << std::endl; }
Y(Y const &)
{ std::cout << "Y::Y(Y const &)" << std::endl; }
Y(BOOST_RV_REF( Y ))
{ std::cout << "Y::Y(Y&&)" << std::endl; }
Y& operator=(BOOST_COPY_ASSIGN_REF( Y ))
{
std::cout << "Y::operator=(Y const &)" << std::endl;
return *this;
}
Y& operator=(BOOST_RV_REF( Y ))
{
std::cout << "Y::operator=(Y&&)" << std::endl;
return *this;
}
static Y make()
{ return Y(); }
};
} // namespace
int main(int argc, char* argv[])
{
std::cout << "Constructing x, cx, y, cy..." << std::endl;
X x;
X const cx;
Y y;
Y const cy;
std::cout << "Constructing ax..." << std::endl;
A ax(x);
std::cout << "Constructing acx..." << std::endl;
A acx(cx);
std::cout << "Constructing arx..." << std::endl;
A arx(X::make());
std::cout << "Constructing ay..." << std::endl;
A ay(y);
std::cout << "Constructing acy..." << std::endl;
A acy(cy);
std::cout << "Constructing ary..." << std::endl;
A ary(Y::make());
return 0;
}
----------------
(The print statements are just to verify expected behavior.)
So, with a 1 argument constructor, you can catch rvalues of
move-emulation-enabled types using the above technique. You could extend
this to 2 or 3 arguments, but it looks like it would take some binding and
overload acrobatics to cover all the possibilities. If this looks like an
acceptable solution, I can help with adding the requisite boilerplate
(maybe some Boost.PP magic can make this manageable) to Boost.Thread.
Note: This isn't as slick, efficient, or easy-to-implement as Dan's
solution, but it doesn't rely on non-standard behavior and, perhaps, is
safer regarding volatile move-emulation-enabled types.
- Jeff
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk