diff --git a/src/shared/akranges.h b/src/shared/akranges.h index d6923df06..055a40bc3 100644 --- a/src/shared/akranges.h +++ b/src/shared/akranges.h @@ -1,380 +1,372 @@ /* - Copyright (C) 2018 Daniel Vrátil + Copyright (C) 2018 - 2019 Daniel Vrátil 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_AKRANGES_H #define AKONADI_AKRANGES_H #ifndef QT_STRICT_ITERATORS // Without strict iterator QVector::iterator is just a typedef to T*, // which breaks some of the template magic below. QT_STRICT_ITERATORS // are a good thing anyway... #error AkRanges requires QT_STRICT_ITERATORS to be enabled. #endif +#include "aktraits.h" + #include #include #include #include #include #include #include namespace Akonadi { namespace detail { template class Cont> struct To_ { template using Container = Cont; }; -template -OutContainer copyContainer(const InContainer &in, std::true_type) +template && AkTraits::isReservable) + > +OutContainer copyContainer(const InContainer &in) { OutContainer rv; rv.reserve(in.size()); for (auto &&v : in) { rv.push_back(std::move(v)); } return rv; } -template -OutContainer copyContainer(const InContainer &in, std::false_type) +template) + > +OutContainer copyContainer(const InContainer &in) { OutContainer rv; for (const auto &v : in) { - rv.insert(v); // can't use std::inserter on QSet, sadly :/ + rv.insert(v); } return rv; } -template -using void_type = void; - -template class, typename = void_type<>> -struct has_method: std::false_type -{}; +template +using void_t = void; -template class Op> -struct has_method>>: std::true_type -{}; - -template -using push_back = decltype(std::declval().push_back({})); +template +struct transformType; template -struct transformType +struct transformType()(std::declval()))>> { - using type = decltype(std::declval()(Arg{})); -}; - -template -struct transformType -{ - using type = Arg; + using type = decltype(std::declval()(std::declval())); }; template struct transformIteratorType { using type = typename transformType::type; }; template using transformIteratorType_t = typename transformIteratorType::type; template struct IteratorBase { public: using iterator_category = typename Iterator::iterator_category; using value_type = typename Iterator::value_type; using difference_type = typename Iterator::difference_type; using pointer = typename Iterator::pointer; using reference = typename Iterator::reference; IteratorBase(const IteratorBase &other) : mIter(other.mIter) {} IterImpl &operator++() { ++static_cast(this)->mIter; return *static_cast(this); } IterImpl operator++(int) { auto ret = *static_cast(this); ++static_cast(this)->mIter; return ret; } bool operator==(const IterImpl &other) const { return mIter == other.mIter; } bool operator!=(const IterImpl &other) const { return !(*static_cast(this) == other); } bool operator<(const IterImpl &other) const { return mIter < other.mIter; } auto operator-(const IterImpl &other) const { return mIter - other.mIter; } auto operator*() const { return *mIter; } protected: IteratorBase(const Iterator &iter) : mIter(iter) {} Iterator mIter; }; template > struct TransformIterator : public IteratorBase, Iterator> { public: using value_type = IteratorValueType; using pointer = IteratorValueType *; // FIXME: preserve const-ness using reference = const IteratorValueType &; // FIXME: preserve const-ness TransformIterator(const Iterator &iter, const TransformFn &fn) : IteratorBase, Iterator>(iter) , mFn(fn) {} auto operator*() const { return mFn(*(this->mIter)); } private: TransformFn mFn; }; template class FilterIterator : public IteratorBase, Iterator> { public: FilterIterator(const Iterator &iter, const Iterator &end, const Predicate &predicate) : IteratorBase, Iterator>(iter) , mPredicate(predicate), mEnd(end) { while (this->mIter != mEnd && !mPredicate(*this->mIter)) { ++this->mIter; } } FilterIterator &operator++() { if (this->mIter != mEnd) { do { ++this->mIter; } while (this->mIter != mEnd && !mPredicate(*this->mIter)); } return *this; } FilterIterator operator++(int) { auto it = *this; ++(*this); return it; } private: Predicate mPredicate; Iterator mEnd; }; template struct Range { public: using iterator = Iterator; using const_iterator = Iterator; using value_type = typename Iterator::value_type; Range(Iterator &&begin, Iterator &&end) : mBegin(std::move(begin)) , mEnd(std::move(end)) {} Iterator begin() const { return mBegin; } Iterator cbegin() const { return mBegin; } Iterator end() const { return mEnd; } Iterator cend() const { return mEnd; } auto size() const { return mEnd - mBegin; } private: Iterator mBegin; Iterator mEnd; }; - template using IsRange = typename std::is_same>; template struct Transform_ { - Transform_(const TransformFn &fn) - : mFn(fn) - {} - const TransformFn &mFn; }; template struct Filter_ { - Filter_(const PredicateFn &fn) - : mFn(fn) - {} - const PredicateFn &mFn; }; } // namespace detail } // namespace Akonadi // Generic operator| for To_<> convertor template class OutContainer, typename T = typename InContainer::value_type > auto operator|(const InContainer &in, const Akonadi::detail::To_ &) -> OutContainer { using namespace Akonadi::detail; - return copyContainer>( - in, has_method, push_back>{}); + return copyContainer>(in); } // Specialization for case when InContainer and OutContainer are identical +// Create a copy, but for Qt container this is very cheap due to implicit sharing. template class InContainer, typename T > auto operator|(const InContainer &in, const Akonadi::detail::To_ &) -> InContainer { return in; } // Generic operator| for transform() template auto operator|(const InContainer &in, const Akonadi::detail::Transform_ &t) { using namespace Akonadi::detail; using OutIt = TransformIterator; return Range(OutIt(in.cbegin(), t.mFn), OutIt(in.cend(), t.mFn)); } // Generic operator| for filter() template auto operator|(const InContainer &in, const Akonadi::detail::Filter_ &p) { using namespace Akonadi::detail; using OutIt = FilterIterator; return Range(OutIt(in.cbegin(), in.cend(), p.mFn), OutIt(in.cend(), in.cend(), p.mFn)); } namespace Akonadi { +/// Non-lazily convert given range or container to QVector static constexpr auto toQVector = detail::To_{}; +/// Non-lazily convert given range or container to QSet static constexpr auto toQSet = detail::To_{}; +/// Non-lazily convert given range or container to QList static constexpr auto toQList = detail::To_{}; +/// Lazily transform each element of a range or container using given transformation template detail::Transform_ transform(TransformFn &&fn) { - return detail::Transform_(std::forward(fn)); + return detail::Transform_{std::forward(fn)}; } +/// Lazily filters a range or container by applying given predicate on each element template detail::Filter_ filter(PredicateFn &&fn) { - return detail::Filter_(std::forward(fn)); + return detail::Filter_{std::forward(fn)}; } +/// Create a range, a view on a container from the given pair fo iterators template > detail::Range range(Iterator1 begin, Iterator2 end) { return detail::Range(std::move(begin), std::move(end)); } } // namespace Akonadi #endif diff --git a/src/shared/aktraits.h b/src/shared/aktraits.h new file mode 100644 index 000000000..0393a4b28 --- /dev/null +++ b/src/shared/aktraits.h @@ -0,0 +1,163 @@ +/* + Copyright (C) 2019 Daniel Vrátil + + 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_AKTRAITS_H_ +#define AKONADI_AKTRAITS_H_ + +#include +#include + +namespace Akonadi { +namespace AkTraits { + +namespace detail { + + /// Helpers from C++17 + template + using void_t = void; + + template + struct conjunction : std::true_type {}; + template + struct conjunction : T {}; + template + struct conjunction : std::conditional_t, T> {}; + + /// Check for presence of member type + template> + struct hasMember { + static constexpr bool value = false; + }; + + template + struct hasMember> : std::true_type {}; + + /// TODO: Use Boost TTI instead? + #define DECLARE_HAS_METHOD_GENERIC_IMPL(name, fun, sign) \ + template \ + struct hasMethod_##name { \ + public: \ + template \ + struct helperClass; \ + \ + using True = char; \ + using False = struct { char dummy_[2]; }; \ + \ + template \ + static True helper(helperClass*);\ + template \ + static False helper(...); \ + public: \ + static constexpr bool value = sizeof(helper(nullptr)) == sizeof(True); \ + }; + + #define DECLARE_HAS_METHOD_GENERIC_CONST(fun, R, ...) \ + DECLARE_HAS_METHOD_GENERIC_IMPL(fun##_const, fun, R(T::*)(__VA_ARGS__) const) + + #define DECLARE_HAS_METHOD_GENERIC(fun, R, ...) \ + DECLARE_HAS_METHOD_GENERIC_IMPL(fun, fun, R(T::*)(__VA_ARGS__)) + + DECLARE_HAS_METHOD_GENERIC_CONST(size, int) + DECLARE_HAS_METHOD_GENERIC(push_back, void, const typename T::value_type &) + DECLARE_HAS_METHOD_GENERIC(insert, typename T::iterator, const typename T::value_type &) + DECLARE_HAS_METHOD_GENERIC(reserve, void, int) + + #define DECLARE_HAS_FUNCTION(name, fun) \ + template \ + struct has_##name { \ + template \ + struct helperClass; \ + \ + using True = char; \ + using False = struct { char dummy_[2]; }; \ + \ + template \ + static True helper(helperClass()))>*); \ + template \ + static False helper(...); \ + public: \ + static constexpr bool value = sizeof(helper(nullptr)) == sizeof(True); \ + }; + + // For some obscure reason QVector::begin() actually has a default + // argument, but QList::begin() does not, thus a regular hasMethod_* check + // won't cut it here. Instead we check whether the container object can be + // used with std::begin() and std::end() helpers. + // Check for constness can be performed by passing "const T" to the type. + DECLARE_HAS_FUNCTION(begin, std::begin) + DECLARE_HAS_FUNCTION(end, std::end) + + /// This is a very incomplete set of Container named requirement, but I'm + /// too lazy to implement all of them, but this should be good enough to match + /// regular Qt containers and /not/ match arbitrary non-container types + template + struct isContainer : conjunction< + std::is_constructible, + hasMember, + has_begin, + has_begin, + has_end, + has_end, + hasMethod_size_const + > {}; + + /// Matches anything that is a container and has push_back() method. + template + struct isAppendable : conjunction< + isContainer, + hasMethod_push_back + > {}; + + /// Matches anything that is a container and has insert() method. + template + struct isInsertable : conjunction< + isContainer, + hasMethod_insert + > {}; + + /// Matches anything that is a container and has reserve() method. + template + struct isReservable : conjunction< + isContainer, + hasMethod_reserve + > {}; +} + +template +constexpr bool isAppendable = detail::isAppendable::value; + +template +constexpr bool isInsertable = detail::isInsertable::value; + +template +constexpr bool isReservable = detail::isReservable::value; + +} +} + +#define AK_PP_CAT_(X, Y) X ## Y +#define AK_PP_CAT(X, Y) AK_PP_CAT_(X, Y) + +#define AK_REQUIRES(...) \ + bool AK_PP_CAT(_ak_requires_, __LINE__) = false, \ + std::enable_if_t< \ + AK_PP_CAT(_ak_requires_, __LINE__) || (__VA_ARGS__) \ + >* = nullptr + +#endif