Boost logo

Boost :

Subject: Re: [boost] [non-instrusive move] interest in new library?
From: Thomas Jordan (tomjordan766_at_[hidden])
Date: 2013-01-15 14:13:22


On 11/01/2013 09:58, Thomas Jordan wrote:
> Jeffrey Lee Hellrung, Jr. wrote
>> You didn't really expound on the implementation, but, if I had to guess,
>> you'd also require your types to be default constructible, and it would
>> perform suboptimally (worse than leaving the move call out) if the swap
>> member function was equivalent to std::swap. I think this might preclude
>> its use in generic contexts and limit its use to situations where you
>> *know* the type is std::vector-like. Let me know if I'm presuming
>> incorrectly :)
>>
> Yes, it certainly requires the types to be default constructible.
>
> For a type with an expensive swap, performance would be suboptimal.
> The library checks
> at compile-time whether the element type of the boost array has a swap
> member, and
> calls array.swap if true, and copies otherwise (recursively if the
> element type is also a
> boost array). However, the user would need to write an overload for a
> type with an
> expensive member swap, which precludes the true generic context. So
> yes, it is more
> aimed at situations where you have pretty firm knowledge of the types
> you will be
> using. This would either require a really strong caveat emptor or may
> not be acceptable
> for release into the wild?
>
> The alternative would be to have just the single, default, template move
> function:
>
> //pseudo-code
> move(T& left, T& right)
>
> plus a pair of template functions to move to and from temporaries
> respectively:
>
> T move(T& x)
> void move(const T& from, T& to)
>
> then have optimised move functions defined on a per-type basis, e.g.,
>
> MyClass
> {
> void swap(MyClass& other){}
> friend void swap(MyClass& a, MyClass& b)
> {
> //efficient swap member
> }
> friend void move(MyClass& lhs, MyClass& rhs)
> {
> lhs.swap(rhs);
> }
> }
>
> as well as overloads defined for any cheap-to-swap library types
> interested in:
>
> void move(std::vector& lhs, std::vector& rhs){}
> void move(boost::function& lhs, boost::function& rhs){}
> etc.
>
> Although this is safer in that it should ensure optimality - and is
> actually where
> I started -
Here is the code for the safer version:

//SimpleMove.h
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_scalar.hpp>
#include <iterator>
#include <algorithm>

//namespace for overloads of move() function
namespace Overloads
{
     //Default - move as copy
     template <typename T>
     inline void move(T& from, T& to)
     {
         T tmp(from);
         from = to;
         to = tmp;
     }
}

//namespace for implementation functions
namespace SimpleMove_Impl
{
     template <typename T>
     inline void move_impl(T& from, T& to)
     {
         using ::Overloads::move; //bring this into scope
         move(from, to);
     }

     template<typename I, typename O>
     inline O move_impl(I f, I l, O result, ::boost::true_type)
     {
         return std::copy(f,l,result);
     }

     template<typename I, typename O>
     inline O move_impl(I f, I l, O result, ::boost::false_type)
     {
         while (f != l)
         {
             ::SimpleMove::iter_move(f, result);
              ++f; ++result;
         }
         return result;
     }
}

//Namespace for interface functions
namespace SimpleMove
{
     //move a value between two temporaries
     template <typename T>
     inline void move(T& from, T& to)
     {
         ::SimpleMove_Impl::move_impl(from, to);
     }

     //move an lvalue to a temporary
     template <typename T>
     inline T move(T& x)
     {
         T tmp;
         ::SimpleMove_Impl::move_impl(x,tmp);
         return tmp;
     }

     //convenience function to more between iterators
     template<typename T>
     inline void iter_move(T it1, T it2)
     {
         ::SimpleMove_Impl::move_impl(*it1, *it2);
     }

     //move algorithm - could also have move_backward similarly
     template<typename I, typename O>
     inline O move(I f, I l, O result)
     {
         //dispatches to std::copy for scalar
         return ::SimpleMove_Impl::move_impl(f, l, result,
::boost::is_scalar<std::iterator_traits<I>::value_type>::type());
     }

     //overload for built-in array
     template<class T, std::size_t N>
     inline void move(T (& left)[N], T (& right)[N])
     {
         ::SimpleMove::move(left, (left + N), right);
     }
}

//utility to quickly define move functions for user-defined type in
//terms of swap
#define MAKE_MOVEABLE_USING_SWAP(X) \
inline void move(X& from)\
{\
     this->swap(from);\
}\
inline friend void move(X& from, X& to)\
{\
     to.move(from);\
}

//MoveOverloads.h
#include <vector>
#include <string>
#include <boost/array.hpp>

//Add additional overloads you require, for e.g., types in std:: or
boost::, to
//Overloads namespace in the examples of std:: containers below,
//implemented in terms of swap
namespace Overloads
{
     template<typename T>
     inline void move(std::vector<T>& left, std::vector<T>& right)
     {
         left.swap(right);
     }

     inline void move(std::string& left, std::string& right)
     {
         left.swap(right);
     }

     template<typename T, size_t N>
     inline void move(boost::array<T,N>& left, boost::array<T,N>& right)
     {
         SimpleMove::move(left.begin(), left.end(), right.begin());
     }
}

This could be useful as it provides a support for basic move semantics
via a very
small library, enabling 'moving' of your own types via custom move
functions and
e.g., of C++03 std:: containers via their swap functions, in conjunction
with (N)RVO and
copy-elision.

Tom.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk