|
Boost : |
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.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk