$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [non-instrusive move] interest in new library?
From: Thomas Jordan (tomjordan766_at_[hidden])
Date: 2013-01-21 07:20:07
On 15/01/2013 19:13, Thomas Jordan wrote:
> 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.
Here's a newer version of the code, which uses trait 'has_move' to make 
some optimisations,
e.g., ensuring that move(T&) is 'optimal' for a type without an 
optimised move function.
This library is intended as a simple set of utilities to help support 
some of the
techniques discussed in, e.g., 
http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
(in particular for C++03-restricted environments).
//MoveFunctions.h
#include "MoveOverloads.h"
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <iterator>
#include <algorithm>
//Type traits to determine whether a type has a move function defined in 
Overloads namespace or in class namespace.
//This trait class uses SFINAE and "sizeof trick" from the book "C++ 
Template Metaprogramming" by Abrahams and
//Gurtovoy
namespace friend_move_trait
{
         template<typename T>
         struct has_move
         {
             typedef char yes;
             typedef char (&no)[2];
             //SFINAE eliminates this when the type is invalid
             template <typename U, U>
             struct Check;
             template <typename U>
             static yes Tester(Check<void(*)(U&, U&), &move>*);
             //overload resolution prefers anything at all over ...
             template <typename U> static no Tester(...);
             static bool const value = sizeof(Tester<T>(0)) == sizeof(yes);
         };
}
namespace overload_move_trait
{
         template<typename T>
         struct has_move
         {
             typedef char yes;
             typedef char (&no)[2];
             //SFINAE eliminates this when the type is invalid
             template <typename U, U>
             struct Check;
             template <typename U>
             static yes Tester(Check<void(*)(U&, U&), &Overloads::move>*);
             //overload resolution prefers anything at all over ...
             template <typename U> static no Tester(...);
             static bool const value = sizeof(Tester<T>(0)) == sizeof(yes);
         };
}
namespace move_trait
{
     template<typename T>
     struct has_move
     {
         static bool const value = 
overload_move_trait::has_move<T>::value || 
friend_move_trait::has_move<T>::value;
     };
}
namespace Default
{
     template <typename T>
     inline void move(T& from, T& to)
     {
         T tmp(from);
         from = to;
         to = tmp;
     }
}
namespace SimpleMove_Impl
{
     template <typename T>
     inline void move_impl(T& from, T& to)
     {
         using ::Default::move;
         using ::Overloads::move;
         move(from, to);
     }
     //move iteratees if an optimal move function exists for I
     template<typename I, typename O>
     inline O move_impl(I f, I l, O result, ::boost::true_type)
     {
         while (f != l)
         {
             ::SimpleMove::iter_move(f, result);
              ++f; ++result;
         }
         return result;
     }
     //std::copy iteratees if no optimal move function exists for I
     template<typename I, typename O>
     inline O move_impl(I f, I l, O result, ::boost::false_type)
     {
         return std::copy(f,l,result);
     }
}
namespace SimpleMove
{
     template <typename T>
     inline void move(T& from, T& to)
     {
         ::SimpleMove_Impl::move_impl(from, to);
     }
     //move an lvalue to a temporary if T has a move function defined
     template <typename T>
     inline T move(T& x, typename 
boost::enable_if<::move_trait::has_move<T>>::type* = 0)
     {
         T tmp;
         ::SimpleMove_Impl::move_impl(x,tmp);
         return tmp;
     }
     //pass lvalue through if no move function is defined for T
     template <typename T>
     inline T& move(T& x, typename 
boost::disable_if<::move_trait::has_move<T>>::type* = 0)
     {
         return x;
     }
     template<typename T>
     inline void iter_move(T it1, T it2)
     {
         ::SimpleMove_Impl::move_impl(*it1, *it2);
     }
     template<typename I, typename O>
     inline O move(I f, I l, O result)
     {
         return ::SimpleMove_Impl::move_impl(f, l, result,
           ::boost::integral_constant<bool, 
::move_trait::has_move<std::iterator_traits<I>::value_type>::value>::type());
     }
     template<class T, std::size_t N>
     inline void move(T (& left)[N], T (& right)[N])
     {
         ::SimpleMove::move(left, (left + N), right);
     }
}
//utility to define move function 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
//
//This file is just a collection of #includes
#include "SomeProvidedOverloads.h"
//SomeProvidedOverloads.h
//
#include <boost/array.hpp>
#include <vector>
#include <string>
#include <deque>
namespace Overloads
{
     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());
     }
     inline void move(std::string& left, std::string& right)
     {
         left.swap(right);
     }
     template<typename T>
     inline void move(std::vector<T>& left, std::vector<T>& right)
     {
         left.swap(right);
     }
     template<typename T>
     inline void move(std::deque<T>& left, std::deque<T>& right)
     {
         left.swap(right);
     }
}
T.