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().
This commit is contained in:
Eric Vantillard 2022-10-04 13:03:15 +02:00 committed by GitHub
parent eef3605297
commit af98e175e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 230 additions and 1 deletions

View File

@ -604,7 +604,191 @@ namespace etl
return etl::move_iterator<TIterator>(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 TContainer>
class back_insert_iterator : public iterator<output_iterator_tag, void, void, void, void>
{
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<T>.
* @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 <class TContainer>
ETL_NODISCARD ETL_CONSTEXPR17 inline ETL_OR_STD::back_insert_iterator<TContainer> back_inserter(TContainer& container)
{
return ETL_OR_STD::back_insert_iterator<TContainer>(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 <typename TContainer>
class front_insert_iterator
: public iterator<output_iterator_tag, void, void, void, void>
{
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<T>.
* @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 <typename TContainer>
ETL_NODISCARD ETL_CONSTEXPR17 inline ETL_OR_STD::front_insert_iterator<TContainer> front_inserter(TContainer& container)
{
return ETL_OR_STD::front_insert_iterator<TContainer>(container);
}
//***************************************************************************
// Helper templates.

View File

@ -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)
{

View File

@ -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)
{