Commit 2e8819b6 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

Make akRanges work with any callable

parent b70cc3e6
......@@ -26,6 +26,48 @@
using namespace Akonadi;
namespace {
int transformFreeFunc(int i)
{
return i * 2;
}
struct TransformHelper
{
public:
static int transform(int i)
{
return transformFreeFunc(i);
}
int operator()(int i) const
{
return transformFreeFunc(i);
}
};
bool filterFreeFunc(int i)
{
return i % 2 == 0;
}
struct FilterHelper
{
public:
static bool filter(int i)
{
return filterFreeFunc(i);
}
bool operator()(int i)
{
return filterFreeFunc(i);
}
};
}
class AkRangesTest : public QObject
{
Q_OBJECT
......@@ -66,6 +108,9 @@ private Q_SLOTS:
QList<int> in = { 1, 2, 3, 4, 5 };
QList<int> out = { 2, 4, 6, 8, 10 };
QCOMPARE(in | transform([](int i) { return i * 2; }) | toQList, out);
QCOMPARE(in | transform(transformFreeFunc) | toQList, out);
QCOMPARE(in | transform(&TransformHelper::transform) | toQList, out);
QCOMPARE(in | transform(TransformHelper()) | toQList, out);
}
private:
......@@ -148,7 +193,10 @@ private Q_SLOTS:
{
QList<int> in = { 1, 2, 3, 4, 5, 6 };
QList<int> out = { 6, 8, 10 };
QCOMPARE(range(in.begin() + 2, in.begin() + 5) | transform([](int i) { return i * 2; }) | toQList, out);
QCOMPARE(range(in.begin() + 2, in.begin() + 5)
| transform([](int i) { return i * 2; })
| toQList,
out);
}
}
......@@ -169,6 +217,9 @@ private Q_SLOTS:
QCOMPARE(in | filter([](int i) { return i % 2 == 0; })
| toQList,
out);
QCOMPARE(in | filter(filterFreeFunc) | toQList, out);
QCOMPARE(in | filter(&FilterHelper::filter) | toQList, out);
QCOMPARE(in | filter(FilterHelper()) | toQList, out);
}
}
......@@ -177,12 +228,12 @@ private Q_SLOTS:
{
QStringList in = { QStringLiteral("foo"), QStringLiteral("foobar"), QStringLiteral("foob") };
QList<int> out = { 6 };
QCOMPARE(in | transform([](const auto &str) { return str.size(); })
QCOMPARE(in | transform(&QString::size)
| filter([](int i) { return i > 5; })
| toQList,
out);
QCOMPARE(in | filter([](const auto &str) { return str.size() > 5; })
| transform([](const auto &str) { return str.size(); })
| transform(&QString::size)
| toQList,
out);
}
......@@ -228,6 +279,29 @@ private Q_SLOTS:
out);
}
private:
struct ForEachCallable
{
public:
ForEachCallable(QList<int> &out)
: mOut(out) {}
void operator()(int i) {
mOut.push_back(i);
}
static void append(int i) {
sOut.push_back(i);
}
static void clear() {
sOut.clear();
}
static QList<int> sOut;
private:
QList<int> &mOut;
};
private Q_SLOTS:
void testForEach()
{
const QList<int> in = { 1, 2, 3, 4, 5, 6 };
......@@ -236,6 +310,16 @@ private Q_SLOTS:
in | forEach([&out](int v) { out.push_back(v); });
QCOMPARE(out, in);
}
{
QList<int> out;
in | forEach(ForEachCallable(out));
QCOMPARE(out, in);
}
{
ForEachCallable::clear();
in | forEach(&ForEachCallable::append);
QCOMPARE(ForEachCallable::sOut, in);
}
{
QList<int> out;
QCOMPARE(in | forEach([&out](int v) { out.push_back(v); })
......@@ -249,6 +333,8 @@ private Q_SLOTS:
}
};
QList<int> AkRangesTest::ForEachCallable::sOut;
QTEST_GUILESS_MAIN(AkRangesTest)
#include "akrangestest.moc"
/*
Copyright (C) 2018 - 2019 Daniel Vrátil <dvratil@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#ifndef AKONADI_AKHELPERS_H_
#define AKONADI_AKHELPERS_H_
#include <type_traits>
#include <utility>
namespace Akonadi
{
namespace detail
{
// C++14-compatible implementation of std::invoke(), based on implementation
// from https://en.cppreference.com/w/cpp/utility/functional/invoke
template<typename T>
struct is_reference_wrapper : std::false_type {};
template<typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template<typename T>
constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value;
template<typename T>
constexpr bool is_member_function_pointer_v = std::is_member_function_pointer<std::remove_cv_t<std::remove_reference_t<T>>>::value;
template<typename T, typename Type, typename T1, typename ... Args>
auto invoke(Type T::* fun, T1 &&t1, Args && ... args) ->
std::enable_if_t<is_member_function_pointer_v<decltype(fun)>
&& std::is_base_of<T, std::decay_t<T1>>::value,
std::result_of_t<decltype(fun)(T, Args ...)>>
{
return (std::forward<T1>(t1).*fun)(std::forward<Args>(args) ...);
}
template<typename T, typename Type, typename T1, typename ... Args>
auto invoke(Type T::* fun, T1 &&t1, Args && ... args) ->
std::enable_if_t<is_member_function_pointer_v<decltype(fun)>
&& is_reference_wrapper_v<std::decay_t<T1>>,
std::result_of_t<decltype(fun)(T, Args ...)>>
{
return (t1.get().*fun)(std::forward<Args>(args) ...);
}
template<typename T, typename Type, typename T1, typename ... Args>
auto invoke(Type T::* fun, T1 &&t1, Args && ... args) ->
std::enable_if_t<is_member_function_pointer_v<decltype(fun)>
&& !std::is_base_of<T, std::decay_t<T1>>::value
&& !is_reference_wrapper_v<std::decay_t<T1>>,
std::result_of_t<decltype(fun)(T1, Args ...)>>
{
return ((*std::forward<T1>(t1)).*fun)(std::forward<Args>(args) ...);
}
template<typename T, typename Type, typename T1, typename ... Args>
auto invoke(Type T::* fun, T1 &&t1, Args && ... args) ->
std::enable_if_t<!is_member_function_pointer_v<decltype(fun)>
&& std::is_base_of<T1, std::decay_t<T1>>::value,
std::result_of_t<decltype(fun)(T1, Args ...)>>
{
return std::forward<T1>(t1).*fun;
}
template<typename T, typename Type, typename T1, typename ... Args>
auto invoke(Type T::* fun, T1 &&t1, Args && ... args) ->
std::enable_if_t<!is_member_function_pointer_v<decltype(fun)>
&& is_reference_wrapper_v<std::decay_t<T1>>,
std::result_of_t<decltype(fun)(T1, Args ...)>>
{
return t1.get().*fun;
}
template<typename T, typename Type, typename T1, typename ... Args>
auto invoke(Type T::* fun, T1 &&t1, Args && ... args) ->
std::enable_if_t<!is_member_function_pointer_v<decltype(fun)>
&& !std::is_base_of<T1, std::decay_t<T1>>::value
&& !is_reference_wrapper_v<std::decay_t<T1>>,
std::result_of_t<decltype(fun)(T1, Args ...)>>
{
return (*std::forward<T1>(t1)).*fun;
}
template<typename Fun, typename ... Args>
auto invoke(Fun &&fun, Args && ... args)
{
return std::forward<Fun>(fun)(std::forward<Args>(args) ...);
}
} // namespace detail
template<typename Fun, typename ... Args>
auto invoke(Fun &&fun, Args && ... args)
{
return detail::invoke(std::forward<Fun>(fun), std::forward<Args>(args) ...);
}
} // namespace Akonadi
#endif
......@@ -28,6 +28,7 @@
#endif
#include "aktraits.h"
#include "akhelpers.h"
#include <QVector>
#include <QSet>
......@@ -75,28 +76,6 @@ OutContainer copyContainer(const InContainer &in)
return rv;
}
template<typename ...>
using void_t = void;
template<typename Fn, typename Arg,
typename = void>
struct transformType;
template<typename Fn, typename Arg>
struct transformType<Fn, Arg, void_t<decltype(std::declval<Fn>()(std::declval<Arg>()))>>
{
using type = decltype(std::declval<Fn>()(std::declval<Arg>()));
};
template<typename Fn, typename ValueType>
struct transformIteratorType
{
using type = typename transformType<Fn, ValueType>::type;
};
template<typename Fn, typename ValueType>
using transformIteratorType_t = typename transformIteratorType<Fn, ValueType>::type;
template<typename IterImpl,
typename Container,
typename Iterator = typename Container::const_iterator
......@@ -166,11 +145,22 @@ protected:
template<typename Container,
typename TransformFn,
typename Iterator = typename Container::const_iterator,
typename IteratorValueType = transformIteratorType_t<TransformFn, typename Iterator::value_type>
typename Iterator = typename Container::const_iterator
>
struct TransformIterator : public IteratorBase<TransformIterator<Container, TransformFn>, Container>
{
private:
template<typename ... T>
struct ResultOf;
template<typename R, typename ... Args>
struct ResultOf<R(Args ...)> {
using type = R;
};
template<typename ... Ts>
using FuncHelper = decltype(Akonadi::invoke(std::declval<Ts>() ...))(Ts ...);
using IteratorValueType = typename ResultOf<FuncHelper<TransformFn, typename Iterator::value_type>>::type;
public:
using value_type = IteratorValueType;
using pointer = IteratorValueType *; // FIXME: preserve const-ness
......@@ -179,11 +169,12 @@ public:
TransformIterator(const Iterator &iter, const TransformFn &fn, const Container &container)
: IteratorBase<TransformIterator<Container, TransformFn>, Container>(iter, container)
, mFn(fn)
{}
{
}
auto operator*() const
{
return mFn(*(this->mIter));
return Akonadi::invoke(mFn, *this->mIter);
}
private:
......@@ -203,7 +194,7 @@ public:
: IteratorBase<FilterIterator<Container, Predicate>, Container>(iter, container)
, mPredicate(predicate), mEnd(end)
{
while (this->mIter != mEnd && !mPredicate(*this->mIter)) {
while (this->mIter != mEnd && !Akonadi::invoke(mPredicate, *this->mIter)) {
++this->mIter;
}
}
......@@ -213,7 +204,7 @@ public:
if (this->mIter != mEnd) {
do {
++this->mIter;
} while (this->mIter != mEnd && !mPredicate(*this->mIter));
} while (this->mIter != mEnd && !Akonadi::invoke(mPredicate, *this->mIter));
}
return *this;
}
......@@ -280,19 +271,19 @@ using IsRange = typename std::is_same<T, Range<typename T::iterator>>;
template<typename TransformFn>
struct Transform_
{
const TransformFn &mFn;
TransformFn mFn;
};
template<typename PredicateFn>
struct Filter_
{
const PredicateFn &mFn;
PredicateFn mFn;
};
template<typename EachFun>
struct ForEach_
{
const EachFun &mFn;
EachFun mFn;
};
} // namespace detail
......@@ -355,9 +346,12 @@ template<typename InContainer,
typename EachFun
>
auto operator|(const InContainer &in,
const Akonadi::detail::ForEach_<EachFun> &fun)
Akonadi::detail::ForEach_<EachFun> fun)
{
std::for_each(std::cbegin(in), std::cend(in), fun.mFn);
std::for_each(std::cbegin(in), std::cend(in),
[&fun](const auto &val) {
Akonadi::invoke(fun.mFn, val);
});
return in;
}
......@@ -385,6 +379,7 @@ detail::Filter_<PredicateFn> filter(PredicateFn &&fn)
return detail::Filter_<PredicateFn>{std::forward<PredicateFn>(fn)};
}
/// Non-lazily call EachFun for each element of the container or range
template<typename EachFun>
detail::ForEach_<EachFun> forEach(EachFun &&fn)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment