From af98e175e2682bd18ca9d98aebec248a8df36a3a Mon Sep 17 00:00:00 2001 From: Eric Vantillard Date: Tue, 4 Oct 2022 13:03:15 +0200 Subject: [PATCH] Feature/add back insert iterator (#603) * Add back_inserter implementation - Mainly a copy of the STL implementation found in LLVM. - Add test_back_insert_iterator unit test. * Add documentation and use ETL_OR_STD macro * Add MIT License in header * Move back_insert_iterator into iterator.h * Remove unused code * Strictly follow the C++ STL naming https://en.cppreference.com/w/cpp/iterator/back_insert_iterator Strictly following the C++ STL would have container_ be container. * Make the check for C++11 clearer * Run the unit test only for C++11 * Add front_insert_iterator - Make back_insert_iterator available to C++03 - Add ETL_CONSTEXPR17,ETL_NODISCARD and ETL_USING_CPP11 - Replace std:move usage by etl::move - Update doc * Use explicit namespace for adressof() and move(). --- include/etl/iterator.h | 186 ++++++++++++++++++++++++++++++++++++++++- test/test_list.cpp | 22 +++++ test/test_vector.cpp | 23 +++++ 3 files changed, 230 insertions(+), 1 deletion(-) diff --git a/include/etl/iterator.h b/include/etl/iterator.h index 451c1324..11e62cde 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -604,7 +604,191 @@ namespace etl return etl::move_iterator(itr); } -#endif +#endif //ETL_USING_CPP11 + + //*************************************************************************** + // back_insert_iterator + //*************************************************************************** + + /** + * @brief Turns assignment into insertion. + * + * These are output iterators, constructed from a container-of-T. + * Assigning a T to the iterator appends it to the container using push_back. + * + * @tparam TContainer + */ + template + class back_insert_iterator : public iterator + { + public: + /// A nested typedef for the type of whatever container you used. + typedef TContainer container_type; + + /// The only way to create this %iterator is with a container. + explicit ETL_CONSTEXPR17 back_insert_iterator(TContainer& c) + : container(etl::addressof(c)) + { + } + + /** + * This kind of %iterator doesn't really have a @a position in the + * container (you can think of the position as being permanently at + * the end, if you like). Assigning a value to the %iterator will + * always append the value to the end of the container. + * + * @param value An instance of whatever type container_type::const_reference is; + * presumably a reference-to-const T for container. + * @return This %iterator, for chained operations. + */ + ETL_CONSTEXPR17 back_insert_iterator& operator=(const typename TContainer::value_type& value) + { + container->push_back(value); + return (*this); + } + +#if ETL_USING_CPP11 + ETL_CONSTEXPR17 back_insert_iterator& operator=(typename TContainer::reference value) + { + container->push_back(etl::move(value)); + return (*this); + } +#endif // ETL_USING_CPP11 + + /// Simply returns *this. + ETL_NODISCARD ETL_CONSTEXPR17 back_insert_iterator& operator*() + { + return (*this); + } + + /// Simply returns *this. (This %iterator does not @a move.) + ETL_CONSTEXPR17 back_insert_iterator& operator++() + { + return (*this); + } + + /// Simply returns *this. (This %iterator does not @a move.) + ETL_CONSTEXPR17 back_insert_iterator operator++(int) + { + return (*this); + } + + protected: + TContainer* container; + }; + + /** + * This wrapper function helps in creating back_insert_iterator instances. + * Typing the name of the %iterator requires knowing the precise full + * type of the container, which can be tedious and impedes generic + * programming. Using this function lets you take advantage of automatic + * template parameter deduction, making the compiler match the correct types for you. + * + * @tparam TContainer The container type. + * @param container A container of arbitrary type. + * @return An instance of back_insert_iterator working on @p container. + */ + template + ETL_NODISCARD ETL_CONSTEXPR17 inline ETL_OR_STD::back_insert_iterator back_inserter(TContainer& container) + { + return ETL_OR_STD::back_insert_iterator(container); + } + + //*************************************************************************** + // front_insert_iterator + //*************************************************************************** + + /** + * @brief Turns assignment into insertion. + * + * These are output iterators, constructed from a container-of-T. + * Assigning a T to the iterator prepends it to the container using + * push_front. + * + * Tip: Using the front_inserter function to create these iterators can + * save typing. + * + * @tparam TContainer The container type. + */ + template + class front_insert_iterator + : public iterator + { + public: + /// A nested typedef for the type of whatever container you used. + typedef TContainer container_type; + + /// The only way to create this %iterator is with a container. + explicit ETL_CONSTEXPR17 front_insert_iterator(TContainer& c) + : container(etl::addressof(c)) + { + } + + /** + * This kind of %iterator doesn't really have a @a position in the + * container (you can think of the position as being permanently at + * the front, if you like). Assigning a value to the %iterator will + * always prepend the value to the front of the container. + * + * @param value An instance of whatever type + * container_type::const_reference is; presumably a + * reference-to-const T for container. + * @return This %iterator, for chained operations. + * + */ + ETL_CONSTEXPR17 front_insert_iterator& operator=(const typename TContainer::value_type& value) + { + container->push_front(value); + return (*this); + } + +#if ETL_USING_CPP11 + ETL_CONSTEXPR17 front_insert_iterator& operator=(typename TContainer::reference value) + { + container->push_front(etl::move(value)); + return (*this); + } +#endif // ETL_USING_CPP11 + + /// Simply returns *this. + ETL_NODISCARD ETL_CONSTEXPR17 front_insert_iterator& operator*() + { + return (*this); + } + + /// Simply returns *this. (This %iterator does not @a move.) + ETL_CONSTEXPR17 front_insert_iterator& operator++() + { + return (*this); + } + + /// Simply returns *this. (This %iterator does not @a move.) + ETL_CONSTEXPR17 front_insert_iterator operator++(int) + { + return (*this); + } + + protected: + TContainer* container; + }; + + /** + * This wrapper function helps in creating front_insert_iterator instances. + * Typing the name of the %iterator requires knowing the precise full + * type of the container, which can be tedious and impedes generic + * programming. Using this function lets you take advantage of automatic + * template parameter deduction, making the compiler match the correct + * types for you. + * + * @tparam TContainer The container type. + * @param container A container of arbitrary type. + * @return An instance of front_insert_iterator working on @p x. + */ + template + ETL_NODISCARD ETL_CONSTEXPR17 inline ETL_OR_STD::front_insert_iterator front_inserter(TContainer& container) + { + return ETL_OR_STD::front_insert_iterator(container); + } //*************************************************************************** // Helper templates. diff --git a/test/test_list.cpp b/test/test_list.cpp index 6ac28708..5e3cfd11 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -301,6 +301,28 @@ namespace CHECK(are_equal); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_insert_iterator) + { + DataInt data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + DataInt expected = {81, 64, 49, 36, 25, 16, 9, 4, 1, 0}; + DataInt transformed; + + auto squared = [](int value) + { + return value * value; + }; + + etl::transform(data.cbegin(), data.cend(), etl::front_inserter(transformed), squared); + + CHECK_EQUAL(expected.size(), transformed.size()); + + bool transformed_equals_expected = std::equal(transformed.begin(), + transformed.end(), + expected.begin()); + CHECK(transformed_equals_expected); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_resize_up) { diff --git a/test/test_vector.cpp b/test/test_vector.cpp index 70313f3f..16a9cc76 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -634,6 +634,29 @@ namespace CHECK(is_equal); } + + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_back_insert_iterator) + { + Data data={0,1,2,3,4,5,6,7,8,9}; + Data expected={0,1,4,9,16,25,36,49,64,81}; + Data transformed; + + auto squared = [](int value){ + return value*value; + }; + + etl::transform(data.cbegin(),data.cend(),etl::back_inserter(transformed),squared); + + CHECK_EQUAL(expected.size(), transformed.size()); + + bool transformed_equals_expected = std::equal(transformed.begin(), + transformed.end(), + expected.begin()); + CHECK(transformed_equals_expected); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_emplace_back) {