[Patch] basic rvalue references support
Mathias Gaunard <[email protected]> Sun, 13 Feb 2011 16:45:33 +0100
| Newsgroups | gmane.comp.parsers.spirit.devel |
|---|---|
| Message-ID | <[email protected]> |
The attached patch brings basic rvalue references support to Qi, which allows it to build a tree of movable but non-copiable types, as in the attached example. I have tested that this works correctly with GCC 4.5 in C++0x mode. I believe this is very valuable, since without this feature Qi would keep deep-copying the tree every time; this is the beginning of a much faster Qi. The patch is probably incomplete (changes to assign_to might also be necessary), but it is sufficient for that basic example to work. There are very few changes in Spirit; Fusion was the main culprit. (This patch contains a couple more changes than what I had sent to Hartmut on IRC -- I've also removed variadic templates from the example so that it could compile on MSVC2010) ------------------------------------------------------------------------------ The ultimate all-in-one performance toolkit: Intel(R) Parallel Studio XE: Pinpoint memory and threading errors before they happen. Find and fix more than 250 security defects in the development cycle. Locate bottlenecks in serial and parallel code that limit performance. http://p.sf.net/sfu/intel-dev2devfeb _______________________________________________ Spirit-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/spirit-devel
rvalue_refs.diff
(text/x-patch, 10.1 KB)
Index: boost/fusion/container/vector/detail/vector_n.hpp
===================================================================
--- boost/fusion/container/vector/detail/vector_n.hpp (révision 68808)
+++ boost/fusion/container/vector/detail/vector_n.hpp (copie de travail)
@@ -9,8 +9,24 @@
#if !defined(FUSION_MACRO_05042005)
#define FUSION_MACRO_05042005
+#ifdef BOOST_NO_RVALUE_REFERENCES
+
+#define FUSION_MEMBER_INIT(z, n, _) m##n(_##n)
+#define FUSION_MEMBER_BASE_INIT(z, n, _) _##n
+
+#else
+
+#define FUSION_MOVE_PARAM(z, n, data) std::move(BOOST_PP_CAT(data, n))
+#define FUSION_MEMBER_INIT(z, n, _) m##n(std::forward<Arg##n>(_##n))
+#define FUSION_MEMBER_BASE_INIT(z, n, _) std::forward<Arg##n>(_##n)
+#define FUSION_MOVE_INIT(z, n, _) m##n(std::move(other.m##n))
+
+#define FUSION_MEMBER_MOVE_ASSIGN(z, n, _) \
+ this->BOOST_PP_CAT(m, n) = std::move(vec.BOOST_PP_CAT(m, n));
+
+#endif
+
#define FUSION_MEMBER_DEFAULT_INIT(z, n, _) m##n()
-#define FUSION_MEMBER_INIT(z, n, _) m##n(_##n)
#define FUSION_COPY_INIT(z, n, _) m##n(other.m##n)
#define FUSION_MEMBER_DECL(z, n, _) T##n m##n;
@@ -42,14 +58,28 @@
BOOST_PP_CAT(vector_data, N)()
: BOOST_PP_ENUM(N, FUSION_MEMBER_DEFAULT_INIT, _) {}
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ template<BOOST_PP_ENUM_PARAMS(N, typename Arg)>
+ #endif
BOOST_PP_CAT(vector_data, N)(
BOOST_PP_ENUM_BINARY_PARAMS(
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ N, Arg, && _))
+ #else
N, typename detail::call_param<T, >::type _))
+ #endif
: BOOST_PP_ENUM(N, FUSION_MEMBER_INIT, _) {}
BOOST_PP_CAT(vector_data, N)(
BOOST_PP_CAT(vector_data, N) const& other)
: BOOST_PP_ENUM(N, FUSION_COPY_INIT, _) {}
+
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ BOOST_PP_CAT(vector_data, N)(
+ BOOST_PP_CAT(vector_data, N) && other)
+ : BOOST_PP_ENUM(N, FUSION_MOVE_INIT, _) {}
+ #endif
BOOST_PP_CAT(vector_data, N)&
operator=(BOOST_PP_CAT(vector_data, N) const& vec)
@@ -57,6 +87,15 @@
BOOST_PP_REPEAT(N, FUSION_MEMBER_ASSIGN, _)
return *this;
}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ BOOST_PP_CAT(vector_data, N)&
+ operator=(BOOST_PP_CAT(vector_data, N) && vec)
+ {
+ BOOST_PP_REPEAT(N, FUSION_MEMBER_MOVE_ASSIGN, _)
+ return *this;
+ }
+ #endif
template <typename Sequence>
static BOOST_PP_CAT(vector_data, N)
@@ -87,18 +126,32 @@
BOOST_PP_CAT(vector, N)() {}
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ template<BOOST_PP_ENUM_PARAMS(N, typename Arg)>
+ #endif
#if (N == 1)
explicit
#endif
BOOST_PP_CAT(vector, N)(
BOOST_PP_ENUM_BINARY_PARAMS(
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ N, Arg, && _))
+ #else
N, typename detail::call_param<T, >::type _))
- : base_type(BOOST_PP_ENUM_PARAMS(N, _)) {}
+ #endif
+ : base_type(BOOST_PP_ENUM(N, FUSION_MEMBER_BASE_INIT, _)) {}
template <BOOST_PP_ENUM_PARAMS(N, typename U)>
BOOST_PP_CAT(vector, N)(
BOOST_PP_CAT(vector, N)<BOOST_PP_ENUM_PARAMS(N, U)> const& vec)
: base_type(BOOST_PP_ENUM_PARAMS(N, vec.m)) {}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ template <BOOST_PP_ENUM_PARAMS(N, typename U)>
+ BOOST_PP_CAT(vector, N)(
+ BOOST_PP_CAT(vector, N)<BOOST_PP_ENUM_PARAMS(N, U)> && vec)
+ : base_type(BOOST_PP_ENUM(N, FUSION_MOVE_PARAM, vec.m)) {}
+ #endif
template <typename Sequence>
BOOST_PP_CAT(vector, N)(
@@ -116,6 +169,16 @@
BOOST_PP_REPEAT(N, FUSION_MEMBER_ASSIGN, _)
return *this;
}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ template <BOOST_PP_ENUM_PARAMS(N, typename U)>
+ BOOST_PP_CAT(vector, N)&
+ operator=(BOOST_PP_CAT(vector, N)<BOOST_PP_ENUM_PARAMS(N, U)> && vec)
+ {
+ BOOST_PP_REPEAT(N, FUSION_MEMBER_MOVE_ASSIGN, _)
+ return *this;
+ }
+ #endif
template <typename Sequence>
typename disable_if<is_convertible<Sequence, T0>, this_type&>::type
Index: boost/utility/value_init.hpp
===================================================================
--- boost/utility/value_init.hpp (révision 68808)
+++ boost/utility/value_init.hpp (copie de travail)
@@ -22,7 +22,7 @@
// contains. More details on these issues are at libs/utility/value_init.htm
#include <boost/aligned_storage.hpp>
-#include <boost/config.hpp> // For BOOST_NO_COMPLETE_VALUE_INITIALIZATION.
+#include <boost/config.hpp> // For BOOST_NO_COMPLETE_VALUE_INITIALIZATION and BOOST_NO_RVALUE_REFERENCES.
#include <boost/detail/workaround.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/cv_traits.hpp>
@@ -30,6 +30,9 @@
#include <boost/swap.hpp>
#include <cstring>
#include <new>
+#ifndef BOOST_NO_RVALUE_REFERENCES
+#include <utility>
+#endif
#ifdef BOOST_MSVC
#pragma warning(push)
@@ -169,7 +172,23 @@
return x.data() ;
}
+#ifndef BOOST_NO_RVALUE_REFERENCES
+
template<class T>
+T&& get ( initialized<T>&& x )
+{
+ return std::move(x.data()) ;
+}
+
+template<class T>
+T const&& get ( const initialized<T>&& x )
+{
+ return std::move(x.data()) ;
+}
+
+#endif
+
+template<class T>
void swap ( initialized<T> & lhs, initialized<T> & rhs )
{
lhs.swap(rhs) ;
@@ -209,11 +228,6 @@
{
return m_data;
}
-
- operator T&()
- {
- return m_data;
- }
} ;
@@ -229,7 +243,23 @@
return x.data() ;
}
+#ifndef BOOST_NO_RVALUE_REFERENCES
+
template<class T>
+T&& get ( value_initialized<T>&& x )
+{
+ return std::move(x.data()) ;
+}
+
+template<class T>
+T const&& get ( const value_initialized<T>&& x )
+{
+ return std::move(x.data()) ;
+}
+
+#endif
+
+template<class T>
void swap ( value_initialized<T> & lhs, value_initialized<T> & rhs )
{
lhs.swap(rhs) ;
Index: boost/spirit/home/qi/detail/pass_container.hpp
===================================================================
--- boost/spirit/home/qi/detail/pass_container.hpp (révision 68808)
+++ boost/spirit/home/qi/detail/pass_container.hpp (copie de travail)
@@ -102,7 +102,11 @@
if (!r)
{
// push the parsed value into our attribute
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ r = !traits::push_back(attr, std::move(val));
+ #else
r = !traits::push_back(attr, val);
+ #endif
if (r)
f.first = save;
}
Index: boost/spirit/home/support/container.hpp
===================================================================
--- boost/spirit/home/support/container.hpp (révision 68808)
+++ boost/spirit/home/support/container.hpp (copie de travail)
@@ -252,6 +252,11 @@
///////////////////////////////////////////////////////////////////////////
template <typename Container, typename T>
bool push_back(Container& c, T const& val);
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ template <typename Container, typename T>
+ bool push_back(Container& c, T && val);
+ #endif
//[customization_push_back_default
template <typename Container, typename T, typename Enable/* = void*/>
@@ -262,6 +267,14 @@
c.insert(c.end(), val);
return true;
}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ static bool call(Container& c, T && val)
+ {
+ c.insert(c.end(), std::move(val));
+ return true;
+ }
+ #endif
};
//]
@@ -274,6 +287,15 @@
c = Container();
return push_back(boost::get<Container>(c), val);
}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ static bool call(optional<Container>& c, T && val)
+ {
+ if (!c)
+ c = Container();
+ return push_back(boost::get<Container>(c), std::move(val));
+ }
+ #endif
};
namespace detail
@@ -283,12 +305,16 @@
{
typedef bool result_type;
- push_back_visitor(T const& t) : t_(t) {}
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ push_back_visitor(T t) : t_(std::forward<T>(t)) {}
+ #else
+ push_back_visitor(T t) : t_(t) {}
+ #endif
template <typename Container>
bool push_back_impl(Container& c, mpl::true_) const
{
- return push_back(c, t_);
+ return push_back(c, std::forward<T>(t_));
}
template <typename T_>
@@ -305,7 +331,7 @@
return push_back_impl(c, typename is_container<T_>::type());
}
- T const& t_;
+ T t_;
};
}
@@ -314,15 +340,30 @@
{
static bool call(variant<BOOST_VARIANT_ENUM_PARAMS(T_)>& c, T const& val)
{
- return apply_visitor(detail::push_back_visitor<T>(val), c);
+ return apply_visitor(detail::push_back_visitor<T const&>(val), c);
}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ static bool call(variant<BOOST_VARIANT_ENUM_PARAMS(T_)>& c, T && val)
+ {
+ return apply_visitor(detail::push_back_visitor<T&&>(val), c);
+ }
+ #endif
};
template <typename Container, typename T>
- bool push_back(Container& c, T const& val)
+ bool push_back(Container& c, T const& val)
{
return push_back_container<Container, T>::call(c, val);
}
+
+ #ifndef BOOST_NO_RVALUE_REFERENCES
+ template <typename Container, typename T>
+ bool push_back(Container& c, T && val)
+ {
+ return push_back_container<Container, T>::call(c, std::move(val));
+ }
+ #endif
//[customization_push_back_unused
template <typename Container>
test.cpp
(text/x-c++src, 6.6 KB)
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <vector>
#include <memory>
#include <iostream>
namespace spirit = boost::spirit;
namespace qi = spirit::qi;
namespace phoenix = boost::phoenix;
using namespace std;
struct node;
typedef unique_ptr<node> node_ptr;
#define NODE_MAX_ARITY 10
/* non-copiable node type, dynamic n-ary tree of integers */
struct node
{
private:
void bind_children()
{
for(typename vector<node_ptr>::iterator it = children.begin(); it != children.end(); ++it)
{
(*it)->parent = this;
(*it)->self = &*it;
}
}
node(int value_, vector<node_ptr>&& children_) : value(value_), children(std::move(children_))
{
bind_children();
}
#define M0(z, n, text) \
node(int value_ BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, node_ptr&& child)) : value(value_)\
{ \
boost::array<node_ptr, n> a = { BOOST_PP_ENUM(n, M1, ~) }; \
vector<node_ptr>( \
std::move_iterator<node_ptr*>(a.begin()), \
std::move_iterator<node_ptr*>(a.end()) \
).swap(children); \
\
bind_children(); \
}
#define M1(z, n, text) std::move(child##n)
BOOST_PP_REPEAT(NODE_MAX_ARITY, M0, ~)
#undef M1
#undef M0
public:
/* make_node_ptr friends declarations */
friend node_ptr make_node_ptr(int value, vector<node_ptr>&& children);
#define M0(z, n, text) \
friend node_ptr make_node_ptr(int value BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, node_ptr&& child));
BOOST_PP_REPEAT(NODE_MAX_ARITY, M0, ~)
#undef M0
int value;
node* parent;
node_ptr* self;
vector<node_ptr> children;
void replace(node_ptr& other)
{
other->parent = parent;
*self = std::move(other);
}
bool operator==(const node& other) const
{
if(value != other.value || children.size() != other.children.size())
return false;
for(int i=0; i<children.size(); i++)
if(*children[i] != *other.children[i])
return false;
return true;
}
bool operator!=(const node& other) const
{
return !(*this == other);
}
friend ostream& operator<<(ostream& os, const node& n)
{
static int indent = 0;
cout << "node(" << n.value;
if(n.children.empty())
{
cout << ')';
return os;
}
++indent;
for(auto it = n.children.begin(); it != n.children.end(); ++it)
{
cout << ",\n";
for(int i=0; i<indent; ++i)
cout << " ";
cout << **it;
}
--indent;
cout << '\n';
for(int i=0; i<indent; ++i)
cout << " ";
cout << ')';
return os;
}
};
/* make_node_ptr friends definitions */
node_ptr make_node_ptr(int value, vector<node_ptr>&& children)
{
return node_ptr(new node(value, std::move(children)));
}
#define M0(z, n, text) \
node_ptr make_node_ptr(int value BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, node_ptr&& child))\
{ \
return node_ptr(new node(value BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, M1, ~)));\
}
#define M1(z, n, text) std::move(child##n)
BOOST_PP_REPEAT(NODE_MAX_ARITY, M0, ~)
#undef M1
#undef M0
/* lazy function that constructs and assigns a node_ptr,
* forces move because phoenix can't give us rvalues. */
struct make_node_ptr_impl
{
typedef void result_type;
template<typename A = void, typename B = void, BOOST_PP_ENUM_BINARY_PARAMS(NODE_MAX_ARITY, typename Arg, = void BOOST_PP_INTERCEPT)>
struct result
{
typedef result_type type;
};
result_type operator()(node_ptr& val, int value) const
{
val = make_node_ptr(value);
}
#define M0(z, n, text) \
template<BOOST_PP_ENUM_PARAMS(n, typename Arg)> \
result_type operator()(node_ptr& val, int value, BOOST_PP_ENUM_BINARY_PARAMS(n, Arg, && arg)) const\
{ \
val = make_node_ptr(value, BOOST_PP_ENUM(n, M1, ~)); \
}
#define M1(z, n, text) std::move(arg##n)
BOOST_PP_REPEAT_FROM_TO(1, NODE_MAX_ARITY, M0, ~)
#undef M1
#undef M0
};
phoenix::function<make_node_ptr_impl> make_node_ptr_lazy;
namespace boost { namespace spirit { namespace traits
{
template <typename T>
struct assign_to_attribute_from_value<node_ptr, T>
{
template <typename T_>
static void
call(T_ const& val, node_ptr& attr)
{
attr = make_node_ptr(val);
}
};
}}}
struct grammar : qi::grammar<const char*, qi::space_type, node_ptr()>
{
grammar() : base_type(root)
{
using namespace qi::labels;
int_ = qi::int_;
combine
= ( qi::int_ >> '('
>> (root % ',')
>> ')' )
[ make_node_ptr_lazy(_val, _1, _2) ]
;
root = combine | int_;
}
private:
qi::rule<const char*, qi::space_type, node_ptr()> int_;
qi::rule<const char*, qi::space_type, node_ptr()> combine;
qi::rule<const char*, qi::space_type, node_ptr()> root;
};
int main()
{
const char* input = "1(2(3(4, 5), 6), 7)";
node_ptr root;
qi::phrase_parse(input, input+strlen(input), grammar(), qi::space, root);
node_ptr n =
make_node_ptr(1,
make_node_ptr(2,
make_node_ptr(3,
make_node_ptr(4),
make_node_ptr(5)
),
make_node_ptr(6)
),
make_node_ptr(7)
)
;
/*// some transformation fun
node_ptr& two = n->children[0];
two->replace(two->children[0]);*/
cout << *root << endl;
cout << (*root == *n) << endl;
}