Boost logo

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