Utils: Extend transform for container insertion

You can now return a container and it will be appended to the result
container. That has the drawback of generating temporary container but
if we get them already it makes the code much more readable than a raw
loop.

Change-Id: Ibcd38e85ef759c18cd8da0fac0185f0f6fc123e2
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Marco Bubke
2023-09-28 15:05:25 +02:00
parent 7ae61da78d
commit a4a8fd5b81
2 changed files with 102 additions and 6 deletions

View File

@@ -597,13 +597,91 @@ public:
MapInsertIterator<Container> operator++(int) { return *this; }
};
// inserter helper function, returns a std::back_inserter for most containers
// and is overloaded for QSet<> and other containers without push_back, returning custom inserters
template<typename C>
inline std::back_insert_iterator<C>
inserter(C &container)
// because Qt container are not implementing the standard interface we need
// this helper functions for generic code
template<typename Type>
void append(QList<Type> *container, QList<Type> &&input)
{
return std::back_inserter(container);
container->append(std::move(input));
}
template<typename Type>
void append(QList<Type> *container, const QList<Type> &input)
{
container->append(input);
}
template<typename Container>
void append(Container *container, Container &&input)
{
container->insert(container->end(),
std::make_move_iterator(input.begin()),
std::make_move_iterator(input.end()));
}
template<typename Container>
void append(Container *container, const Container &input)
{
container->insert(container->end(), input.begin(), input.end());
}
// BackInsertIterator behaves like std::back_insert_iterator except is adds the back insertion for
// container of the same type
template<typename Container>
class BackInsertIterator
{
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = ptrdiff_t;
using pointer = void;
using reference = void;
using container_type = Container;
explicit constexpr BackInsertIterator(Container &container)
: m_container(std::addressof(container))
{}
constexpr BackInsertIterator &operator=(const typename Container::value_type &value)
{
m_container->push_back(value);
return *this;
}
constexpr BackInsertIterator &operator=(typename Container::value_type &&value)
{
m_container->push_back(std::move(value));
return *this;
}
constexpr BackInsertIterator &operator=(const Container &container)
{
append(m_container, container);
return *this;
}
constexpr BackInsertIterator &operator=(Container &&container)
{
append(m_container, container);
return *this;
}
[[nodiscard]] constexpr BackInsertIterator &operator*() { return *this; }
constexpr BackInsertIterator &operator++() { return *this; }
constexpr BackInsertIterator operator++(int) { return *this; }
private:
Container *m_container;
};
// inserter helper function, returns a BackInsertIterator for most containers
// and is overloaded for QSet<> and other containers without push_back, returning custom inserters
template<typename Container>
inline BackInsertIterator<Container> inserter(Container &container)
{
return BackInsertIterator(container);
}
template<typename X>

View File

@@ -402,6 +402,24 @@ void tst_Algorithm::transform()
const QHash<int, int> expected({{1, 2}, {2, 3}, {3, 4}, {4, 5}});
QCOMPARE(trans, expected);
}
{
// std::vector -> std::vector appending container
const std::vector<int> v({1, 2, 3, 4});
const auto trans = Utils::transform<std::vector<int>>(v, [](int i) -> std::vector<int> {
return {i, i * 2};
});
const std::vector<int> expected{1, 2, 2, 4, 3, 6, 4, 8};
QCOMPARE(trans, expected);
}
{
// QList -> QList appending container
const QList<int> v({1, 2, 3, 4});
const auto trans = Utils::transform<QList<int>>(v, [](int i) -> QList<int> {
return {i, i * 2};
});
const QList<int> expected{1, 2, 2, 4, 3, 6, 4, 8};
QCOMPARE(trans, expected);
}
}
void tst_Algorithm::sort()