|
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