diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..73294a570d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +doc/autodoc.xml +doc/html/ diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 96601b7222..47e1ebe05d 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -45,3 +45,13 @@ boostbook standalone autodoc pdf:boost.url.prefix=http://www.boost.org/doc/libs/release/libs/property_tree/doc/html ; + +############################################################################### +alias boostdoc + : property_tree + : + : autodoc + : ; +explicit boostdoc ; +alias boostrelease ; +explicit boostrelease ; diff --git a/include/boost/property_tree/detail/ptree_implementation.hpp b/include/boost/property_tree/detail/ptree_implementation.hpp index dd9fd37e26..b959295d7b 100644 --- a/include/boost/property_tree/detail/ptree_implementation.hpp +++ b/include/boost/property_tree/detail/ptree_implementation.hpp @@ -194,6 +194,15 @@ namespace boost { namespace property_tree { } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template inline + basic_ptree::basic_ptree(basic_ptree &&rv) + : m_data(std::move(rv.m_data)), + m_children(new typename subs::base_container(std::move(subs::ch(&rv)))) + { + } +#endif + template basic_ptree & basic_ptree::operator =(const basic_ptree &rhs) @@ -202,6 +211,16 @@ namespace boost { namespace property_tree return *this; } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template + basic_ptree & + basic_ptree::operator =(basic_ptree &&rv) + { + rv.swap(*this); + return *this; + } +#endif + template basic_ptree::~basic_ptree() { @@ -329,6 +348,15 @@ namespace boost { namespace property_tree return iterator(subs::ch(this).insert(where.base(), value).first); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template inline + typename basic_ptree::iterator + basic_ptree::insert(iterator where, value_type &&value) + { + return iterator(subs::ch(this).insert(where.base(), std::move(value)).first); + } +#endif + template template inline void basic_ptree::insert(iterator where, It first, It last) @@ -357,6 +385,15 @@ namespace boost { namespace property_tree return iterator(subs::ch(this).push_front(value).first); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template inline + typename basic_ptree::iterator + basic_ptree::push_front(value_type &&value) + { + return iterator(subs::ch(this).push_front(std::move(value)).first); + } +#endif + template inline typename basic_ptree::iterator basic_ptree::push_back(const value_type &value) @@ -364,6 +401,15 @@ namespace boost { namespace property_tree return iterator(subs::ch(this).push_back(value).first); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template inline + typename basic_ptree::iterator + basic_ptree::push_back(value_type &&value) + { + return iterator(subs::ch(this).push_back(std::move(value)).first); + } +#endif + template inline void basic_ptree::pop_front() { diff --git a/include/boost/property_tree/ptree.hpp b/include/boost/property_tree/ptree.hpp index 9e7b921659..e897fc8d12 100644 --- a/include/boost/property_tree/ptree.hpp +++ b/include/boost/property_tree/ptree.hpp @@ -89,10 +89,19 @@ namespace boost { namespace property_tree /** Creates a node with no children and a copy of the given data. */ explicit basic_ptree(const data_type &data); basic_ptree(const self_type &rhs); + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + basic_ptree(self_type &&rv); +#endif + ~basic_ptree(); /** Basic guarantee only. */ self_type &operator =(const self_type &rhs); +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + self_type &operator =(self_type &&rv); +#endif + /** Swap with other tree. Only constant-time and nothrow if the * data type's swap is. */ @@ -126,6 +135,14 @@ namespace boost { namespace property_tree */ iterator insert(iterator where, const value_type &value); +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + /** Insert a copy of the given tree with its key just before the given + * position in this node. This operation invalidates no iterators. + * @return An iterator to the newly created child. + */ + iterator insert(iterator where, value_type &&value); +#endif + /** Range insert. Equivalent to: * @code * for(; first != last; ++first) insert(where, *first); @@ -150,9 +167,18 @@ namespace boost { namespace property_tree /** Equivalent to insert(begin(), value). */ iterator push_front(const value_type &value); +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + /** Equivalent to insert(begin(), value). */ + iterator push_front(value_type &&value); +#endif + /** Equivalent to insert(end(), value). */ iterator push_back(const value_type &value); +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + /** Equivalent to insert(end(), value). */ + iterator push_back(value_type &&value); +#endif /** Equivalent to erase(begin()). */ void pop_front(); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index af25731d74..4239ee0406 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -24,4 +24,5 @@ test-suite "property_tree" [ run test_xml_parser_rapidxml.cpp ] [ run test_multi_module1.cpp test_multi_module2.cpp ] + [ run test_lifecycle.cpp ] ; diff --git a/test/test_lifecycle.cpp b/test/test_lifecycle.cpp new file mode 100644 index 0000000000..eaa29e2bff --- /dev/null +++ b/test/test_lifecycle.cpp @@ -0,0 +1,253 @@ + +// ---------------------------------------------------------------------------- +// Copyright (C) 2017 Maciej Gajewski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see www.boost.org +// ---------------------------------------------------------------------------- + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// These test are to make sure that no uneccesary copy of storwd object is performend, and that move is prefered over copy +// whenever applicable + +#include +#include + + +template +struct lifecycle_counter +{ + static int default_ctors; + static int copy_ctors; + static int move_ctors; + static int init_ctors; + static int copy_assigments; + static int move_assignments; + static int destructors; + + static void reset_counters() + { + default_ctors = 0; + copy_ctors = 0; + move_ctors = 0; + init_ctors = 0; + copy_assigments = 0; + move_assignments = 0; + destructors = 0; + } + + std::string m_value; + + lifecycle_counter() + { + default_ctors++; + } + + lifecycle_counter(const std::string& from) + : m_value(from) + { + init_ctors++; + } + + lifecycle_counter(const char* from) + : m_value(from) + { + init_ctors++; + } + + lifecycle_counter(const lifecycle_counter& o) + : m_value(o.m_value) + { + copy_ctors++; + } + + lifecycle_counter(lifecycle_counter&& o) + : m_value(std::move(o.m_value)) + { + move_ctors++; + } + + ~lifecycle_counter() + { + destructors++; + } + + lifecycle_counter& operator=(const lifecycle_counter& o) + { + m_value = o.m_value; + copy_assigments++; + return *this; + } + + lifecycle_counter& operator=(lifecycle_counter&& o) + { + m_value = o.m_value; + move_assignments++; + return *this; + } + + bool operator == (const lifecycle_counter& o) const + { + return m_value == o.m_value; + } + + bool operator < (const lifecycle_counter& o) const + { + return m_value < o.m_value; + } +}; + +template int lifecycle_counter::default_ctors = 0; +template int lifecycle_counter::copy_ctors = 0; +template int lifecycle_counter::move_ctors = 0; +template int lifecycle_counter::init_ctors = 0; +template int lifecycle_counter::copy_assigments = 0; +template int lifecycle_counter::move_assignments = 0; +template int lifecycle_counter::destructors = 0; + + +using key_type = lifecycle_counter; +using data_type = lifecycle_counter; + +using test_ptree = boost::property_tree::basic_ptree; + +namespace boost { namespace property_tree { + +template<> +struct path_of +{ + using type = std::string; +}; + +}} + +static void reset_counters() +{ + key_type::reset_counters(); + data_type::reset_counters(); +} + +// static test_ptree make_one_element_tree() +// { +// test_ptree a; +// a.push_back({"key", test_ptree("value")}); +// return a; +// } + +static void test_constructor() +{ + test_ptree a("value"); + + // Copy - expect value to be copied + reset_counters(); + test_ptree b = a; + BOOST_CHECK(data_type::copy_ctors == 1); + + // Move - expect the vaule not to be copied + reset_counters(); + test_ptree c = std::move(a); + BOOST_CHECK(data_type::copy_ctors == 0); + BOOST_CHECK(data_type::copy_assigments == 0); +} + +static void test_assignment() +{ + test_ptree a("a"); + test_ptree b("a"); + + // Copy + reset_counters(); + b = a; + BOOST_CHECK(data_type::copy_ctors == 1); + + // Move + reset_counters(); + b = std::move(a); + BOOST_CHECK(data_type::copy_ctors == 0); + BOOST_CHECK(data_type::copy_assigments == 0); +} + +static void test_push_back() +{ + test_ptree tree; + + // push_back - copy + test_ptree::value_type child1("key1", test_ptree("c1")); + + reset_counters(); + tree.push_back(child1); + + BOOST_CHECK(data_type::copy_ctors == 1); + BOOST_CHECK(key_type::copy_ctors == 1); + + // push_back - move + test_ptree::value_type child2("key2", test_ptree("c2")); + + reset_counters(); + tree.push_back(std::move(child2)); + + BOOST_CHECK(data_type::copy_ctors == 0); + BOOST_CHECK(data_type::copy_assigments == 0); +} + +static void test_push_front() +{ + test_ptree tree; + + // push_back - copy + test_ptree::value_type child1("key1", test_ptree("c1")); + + reset_counters(); + tree.push_front(child1); + + BOOST_CHECK(data_type::copy_ctors == 1); + BOOST_CHECK(key_type::copy_ctors == 1); + + // push_back - move + test_ptree::value_type child2("key2", test_ptree("c2")); + + reset_counters(); + tree.push_front(std::move(child2)); + + BOOST_CHECK(data_type::copy_ctors == 0); + BOOST_CHECK(data_type::copy_assigments == 0); +} + +static void test_insert() +{ + test_ptree tree; + + // push_back - copy + test_ptree::value_type child1("key1", test_ptree("c1")); + + reset_counters(); + tree.insert(tree.begin(), child1); + + BOOST_CHECK(data_type::copy_ctors == 1); + BOOST_CHECK(key_type::copy_ctors == 1); + + // push_back - move + test_ptree::value_type child2("key2", test_ptree("c2")); + + reset_counters(); + tree.insert(tree.begin(), std::move(child2)); + + BOOST_CHECK(data_type::copy_ctors == 0); + BOOST_CHECK(data_type::copy_assigments == 0); +} + +int test_main(int, char *[]) +{ + test_constructor(); + test_push_back(); + test_push_front(); + test_insert(); + test_assignment(); + + return 0; +} + +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES \ No newline at end of file diff --git a/test/test_property_tree.hpp b/test/test_property_tree.hpp index 4216945a15..45fe606076 100644 --- a/test/test_property_tree.hpp +++ b/test/test_property_tree.hpp @@ -91,6 +91,18 @@ void test_constructor_destructor_assignment(PTREE *) BOOST_CHECK(pt1 == *pt3); //BOOST_CHECK(PTREE::debug_get_instances_count() == 15); +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Test move constructor + PTREE pt4(std::move(pt1)); + + BOOST_CHECK(pt4 == *pt2); + BOOST_CHECK(pt1.empty()); + + // Test move assignment + pt1 = std::move(pt4); + BOOST_CHECK(pt1 == *pt2); + BOOST_CHECK(pt4.empty()); +#endif // Destroy delete pt2; //BOOST_CHECK(PTREE::debug_get_instances_count() == 10); @@ -241,7 +253,6 @@ void test_pushpop(PTREE *) pt.pop_front(); //BOOST_CHECK(PTREE::debug_get_instances_count() == 5); BOOST_CHECK(pt.empty()); - } void test_container_iteration(PTREE *)