@@ -0,0 +1,34 @@ | |||||
/*! | |||||
* \file /utl/concepts/concepts.h | |||||
* \brief Concepts main include header | |||||
* | |||||
* Copyright (C) 2018-2019 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_concepts_concepts_h__ | |||||
#define __utl_consepts_concepts_h__ | |||||
#include <utl/core/impl.h> | |||||
#include <utl/concepts/defines.h> | |||||
#include <utl/concepts/stl.h> | |||||
#include <utl/concepts/iterators.h> | |||||
/*! | |||||
* \defgroup concepts | |||||
*/ | |||||
#endif /* __utl_concepts_concepts_h__ */ |
@@ -0,0 +1,69 @@ | |||||
/*! | |||||
* \file /utl/concepts/defines.h | |||||
* \brief Concepts defines | |||||
* | |||||
* Copyright (C) 2018-2019 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_concepts_defines_h__ | |||||
#define __utl_concepts_defines_h__ | |||||
//!\defgroup concepts | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* utl typename constraints wrapper | |||||
* | |||||
* \example | |||||
* \code | |||||
* template <utlConstrainType(SomeConcept) T> struct lala { }; | |||||
* // will expand to something like: | |||||
* // template <SomeConcept T> struct lala { }; | |||||
* // or | |||||
* // template <typename T> struct lala { }; | |||||
* \endcode | |||||
*/ | |||||
#if CXX_CONCEPTS | |||||
#define utlConstrainType(_Concept_) _Concept_ | |||||
#else | |||||
#define utlConstrainType(_Concept_) typename | |||||
#endif | |||||
/*! \brief | |||||
* utl concept keyword syntax wrapper | |||||
*/ | |||||
#if CXX_CONCEPTS | |||||
#if __cpp_concepts <= 201507L | |||||
#define _utlConcept concept bool | |||||
#else | |||||
#define _utlConcept concept | |||||
#endif | |||||
#else | |||||
#define _utlConcept constexpr bool | |||||
#endif | |||||
#ifndef CXX_LIB_INVOKE | |||||
#ifdef __cpp_lib_invoke | |||||
#define CXX_LIB_INVOKE __cpp_lib_invoke | |||||
#else | |||||
#define CXX_LIB_INVOKE 0 | |||||
#endif | |||||
#endif | |||||
//! @} | |||||
#endif /* __utl_concepts_defines_h__ */ |
@@ -0,0 +1,70 @@ | |||||
/*! | |||||
* \file /utl/impl/concepts/iterators.h | |||||
* \brief utl iterator concept support header | |||||
* | |||||
* Copyright (C) 2018 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_concepts_iterator_h__ | |||||
#define __utl_concepts_iterator_h__ | |||||
#include <utl/core/impl.h> | |||||
#include <utl/concepts/defines.h> | |||||
#include <utl/concepts/stl.h> | |||||
/*! | |||||
* \ingroup concepts | |||||
* \defgroup iterators | |||||
*/ | |||||
//! @{ | |||||
namespace utl { | |||||
#if CXX_CONCEPTS | |||||
template <class I> | |||||
_utlConcept WeaklyIncrementable = | |||||
Semiregular<I> && | |||||
requires(I i) { | |||||
{ ++i } -> Same<I>&; // not required to be equality preserving | |||||
i++; // not required to be equality preserving | |||||
}; | |||||
#else | |||||
namespace detail { | |||||
template <typename I> using try_ppI = decltype (++(std::declval<I>())); | |||||
template <typename I> using try_Ipp = decltype (std::declval<I>()++); | |||||
} | |||||
template <class I> | |||||
_utlConcept WeaklyIncrementable = | |||||
Semiregular<I> | |||||
&& Same<_ref_t<I>, meta::detected_t<detail::try_ppI, _ref_t<I>>> | |||||
&& meta::is_detected<detail::try_Ipp, _ref_t<I>>::value; | |||||
#endif | |||||
#if CXX_CONCEPTS | |||||
template <class I> | |||||
_utlConcept DeviceIterator = | |||||
requires(I i) { | |||||
{ *i } -> auto&&; // Requires: i is dereferenceable | |||||
} && | |||||
WeaklyIncrementable<I>; | |||||
#else | |||||
#endif | |||||
} | |||||
//! @} | |||||
#endif /* __utl_concepts_iterator_h__ */ |
@@ -0,0 +1,751 @@ | |||||
/*! | |||||
* \file /utl/concepts/stl.h | |||||
* \brief STL's Concepts | |||||
* | |||||
* Copyright (C) 2018 - 2019 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_concepts_stl_h__ | |||||
#define __utl_concepts_stl_h__ | |||||
#include <utl/core/impl.h> | |||||
#include <utl/meta/meta.h> | |||||
#include <utl/utility/invoke.h> | |||||
#include <utl/concepts/defines.h> | |||||
/*! | |||||
* \brief | |||||
* STL's core language concepts | |||||
* | |||||
* We provide std concepts in case host's stl does not provide them yet. | |||||
* | |||||
* For more information \see https://en.cppreference.com/w/cpp/concepts | |||||
*/ | |||||
//! @{ | |||||
namespace utl { | |||||
template <typename T> | |||||
using remove_cvref_t = std::remove_cv_t< std::remove_reference_t<T> >; | |||||
template <typename T> | |||||
using cref_ = const std::remove_reference_t<T>&; | |||||
template <typename T> | |||||
using _ref_t = std::add_lvalue_reference_t<T>; | |||||
/*! | |||||
* Same | |||||
*/ | |||||
template <class T, class U> | |||||
_utlConcept Same = meta::same_<T, U>::value; | |||||
// template<class T> | |||||
// _utlConcept Decayed = Same<T, std::decay_t<T>>; | |||||
/*! | |||||
* DerivedFrom | |||||
*/ | |||||
template <class Derived, class Base> | |||||
_utlConcept DerivedFrom = | |||||
std::is_base_of<Base, Derived>::value && | |||||
std::is_convertible<const volatile Derived*, const volatile Base*>::value; | |||||
/*! | |||||
* ConvertibleTo | |||||
*/ | |||||
template <class From, class To> | |||||
#if CXX_CONCEPTS | |||||
_utlConcept ConvertibleTo = | |||||
std::is_convertible<From, To>::value && | |||||
requires(From (&f)()) { | |||||
static_cast<To>(f()); | |||||
}; | |||||
#else | |||||
_utlConcept ConvertibleTo = std::is_convertible<From, To>::value; | |||||
#endif | |||||
/*! | |||||
* Common Reference | |||||
*/ | |||||
//! @{ | |||||
namespace common_impl { | |||||
//! \see https://ericniebler.github.io/std/wg21/D0022.html | |||||
// ========== common reference =========== | |||||
template<class T, class U> | |||||
using __cond_res = | |||||
decltype(false ? std::declval<T(&)()>()() : std::declval<U(&)()>()()); | |||||
template<class From> | |||||
struct __copy_cv_ { | |||||
static_assert(!std::is_reference<From>::value); | |||||
template<class To> using apply = To; | |||||
}; | |||||
template<class From> | |||||
struct __copy_cv_<const From> { | |||||
template<class To> using apply = const To; | |||||
}; | |||||
template<class From> | |||||
struct __copy_cv_<volatile From> { | |||||
template<class To> using apply = volatile To; | |||||
}; | |||||
template<class From> | |||||
struct __copy_cv_<const volatile From> { | |||||
template<class To> using apply = const volatile To; | |||||
}; | |||||
template<class From, class To> | |||||
using __copy_cv = meta::invoke<__copy_cv_<From>, To>; | |||||
// CREF [meta.trans.other]/2.1 | |||||
template<class T> | |||||
using __cref = std::add_lvalue_reference_t<const std::remove_reference_t<T>>; | |||||
// COMMON_REF [meta.trans.other]/2 | |||||
template<class T, class U, class = void> | |||||
struct __common_ref_ { | |||||
static_assert(std::is_reference<T>::value, ""); | |||||
static_assert(std::is_reference<U>::value, ""); | |||||
}; | |||||
template<class T, class U> | |||||
using __common_ref = meta::eval<__common_ref_<T, U>>; | |||||
// [meta.trans.other]/2.5 | |||||
template<class T, class U> | |||||
using __lref_res = __cond_res< | |||||
__copy_cv<T, U> &, | |||||
__copy_cv<U, T> & | |||||
>; | |||||
// [meta.trans.other]/2.6 | |||||
template<class T, class U, class R = __common_ref<T&, U&>> | |||||
using __rref_res = std::remove_reference_t<R>&&; | |||||
template<class T, class U> | |||||
struct __common_ref_<T&, U&, | |||||
meta::void_t<__lref_res<T, U>, | |||||
meta::when<std::is_reference<__lref_res<T, U>>::value>> > { | |||||
using type = __lref_res<T, U>; | |||||
}; | |||||
template<class T, class U> | |||||
struct __common_ref_<T&&, U&&, | |||||
meta::void_t<__common_ref<T&, U&>, | |||||
meta::when<ConvertibleTo<T&&, __rref_res<T, U>>>, | |||||
meta::when<ConvertibleTo<U&&, __rref_res<T, U>>>> > { | |||||
using type = __rref_res<T, U>; | |||||
}; | |||||
// [meta.trans.other]/2.7 | |||||
template<class T, class U> | |||||
struct __common_ref_<T&&, U&, | |||||
meta::void_t<__common_ref<const T&, U&>, | |||||
meta::when<ConvertibleTo<T&&, __common_ref<const T&, U&>>>> > { | |||||
using type = __common_ref<const T&, U&>; | |||||
}; | |||||
// [meta.trans.other]/2.8 | |||||
template<class T, class U> | |||||
struct __common_ref_<T&, U&&, | |||||
meta::void_t<__common_ref<T&, const U&>, | |||||
meta::when<ConvertibleTo<U&&, __common_ref<T&, const U&>>>> > { | |||||
using type = __common_ref<T&, const U&>; | |||||
}; | |||||
template<class> | |||||
struct __xref { | |||||
template<class U> using apply = U; | |||||
}; | |||||
template<class T> | |||||
struct __xref<const T> { | |||||
template<class U> using apply = const U; | |||||
}; | |||||
template<class T> | |||||
struct __xref<volatile T> { | |||||
template<class U> using apply = volatile U; | |||||
}; | |||||
template<class T> | |||||
struct __xref<const volatile T> { | |||||
template<class U> using apply = const volatile U; | |||||
}; | |||||
template<class T> | |||||
struct __xref<T&> { | |||||
template<class U> using apply = | |||||
std::add_lvalue_reference_t<meta::invoke<__xref<T>, U>>; | |||||
}; | |||||
template<class T> | |||||
struct __xref<T&&> { | |||||
template<class U> using apply = | |||||
std::add_rvalue_reference_t<meta::invoke<__xref<T>, U>>; | |||||
}; | |||||
template<class, | |||||
class, | |||||
template<class> class, | |||||
template<class> class | |||||
> | |||||
struct basic_common_reference { }; | |||||
template<class T, class U> | |||||
using __basic_common_reference_t = meta::eval< | |||||
basic_common_reference< | |||||
remove_cvref_t<T>, | |||||
remove_cvref_t<U>, | |||||
__xref<T>::template apply, | |||||
__xref<U>::template apply | |||||
> | |||||
>; | |||||
template<class...> | |||||
struct common_reference {}; | |||||
template<class... Ts> | |||||
using common_reference_t = meta::eval< | |||||
common_reference<Ts...> | |||||
>; | |||||
// [meta.trans.other]/5.2 | |||||
template<class T> | |||||
struct common_reference<T> { | |||||
using type = T; | |||||
}; | |||||
// [meta.trans.other]/5.3.4 | |||||
template<class T, class U, class...> | |||||
struct __common_reference3 | |||||
: std::common_type<T, U> {}; | |||||
// [meta.trans.other]/5.3.3 | |||||
template<class T, class U> | |||||
struct __common_reference3<T, U, | |||||
meta::void_t<__cond_res<T, U>>> { | |||||
using type = __cond_res<T, U>; | |||||
}; | |||||
template<class T, class U, class...> | |||||
struct __common_reference2 | |||||
: __common_reference3<T, U> {}; | |||||
// [meta.trans.other]/5.3.2 | |||||
template<class T, class U> | |||||
struct __common_reference2<T, U, | |||||
meta::void_t<__basic_common_reference_t<T, U>>> { | |||||
using type = __basic_common_reference_t<T, U>; | |||||
}; | |||||
template <class T, class U, class...> | |||||
struct __common_reference | |||||
: __common_reference2<T, U> { }; | |||||
template <class T, class U> | |||||
struct __common_reference<T, U, | |||||
meta::when<std::is_reference<T>::value && std::is_reference<U>::value>> { | |||||
using type = __common_ref<T, U>; | |||||
}; | |||||
template<class T, class U> | |||||
struct common_reference<T, U> : __common_reference<T, U> { }; | |||||
// [meta.trans.other]/5.4 | |||||
template<class T, class U, class V, class... W> | |||||
//requires requires { typename common_reference_t<T, U>; } | |||||
struct common_reference<T, U, V, W...> | |||||
: common_reference < | |||||
common_reference_t<T, U>, V, W... | |||||
> {}; | |||||
} | |||||
template<typename...Ts> | |||||
using common_reference = common_impl::common_reference<Ts...>; | |||||
template<typename... Ts> | |||||
using common_reference_t = meta::eval< | |||||
common_reference<Ts...> | |||||
>; | |||||
//! @} | |||||
//FIXME: CommonReference needs better implementation | |||||
template <class T, class U> | |||||
_utlConcept CommonReference = | |||||
Same<common_reference_t<T, U>, common_reference_t<U, T>> && | |||||
ConvertibleTo<T, common_reference_t<T, U>> && | |||||
ConvertibleTo<U, common_reference_t<T, U>>; | |||||
// != std::Common on CommonReference | |||||
template <class T, class U> | |||||
_utlConcept Common = | |||||
#if CXX_CONCEPTS | |||||
Same<std::common_type_t<T, U>, std::common_type_t<U, T>> && | |||||
requires { | |||||
static_cast<std::common_type_t<T, U>>(std::declval<T>()); | |||||
static_cast<std::common_type_t<T, U>>(std::declval<U>()); | |||||
}; | |||||
// } && | |||||
// CommonReference< | |||||
// std::add_lvalue_reference_t<const T>, | |||||
// std::add_lvalue_reference_t<const U>> && | |||||
// CommonReference< | |||||
// std::add_lvalue_reference_t<std::common_type_t<T, U>>, | |||||
// std::common_reference_t< | |||||
// std::add_lvalue_reference_t<const T>, | |||||
// std::add_lvalue_reference_t<const U> | |||||
// > | |||||
// >; | |||||
#else | |||||
// meta::and_ < | |||||
Same<std::common_type_t<T, U>, std::common_type_t<U, T>>; //> | |||||
// meta::bool_<CommonReference< | |||||
// std::add_lvalue_reference_t<const T>, | |||||
// std::add_lvalue_reference_t<const U> | |||||
// >>, | |||||
// meta::bool_< CommonReference< | |||||
// std::add_lvalue_reference_t<std::common_type_t<T, U>>, | |||||
// common_reference_t< | |||||
// std::add_lvalue_reference_t<const T>, | |||||
// std::add_lvalue_reference_t<const U> | |||||
// > | |||||
// >> | |||||
// >::value; | |||||
#endif | |||||
/*! | |||||
* Integral | |||||
*/ | |||||
template <class T> | |||||
_utlConcept Integral = std::is_integral<T>::value; | |||||
/*! | |||||
* Signed Integral | |||||
*/ | |||||
template <class T> | |||||
_utlConcept SignedIntegral = Integral<T> && std::is_signed<T>::value; | |||||
/*! | |||||
* Unsigned Integral | |||||
*/ | |||||
template <class T> | |||||
_utlConcept UnsignedIntegral = Integral<T> && !std::is_signed<T>::value; | |||||
template <typename T> | |||||
_utlConcept MoveAssignable = std::is_move_assignable<T>::value; | |||||
template <typename T> | |||||
_utlConcept CopyAssignable = std::is_copy_assignable<T>::value; | |||||
/*! | |||||
* Assignable | |||||
* \note != std:: on CommonReference | |||||
*/ | |||||
template<class LHS, class RHS> | |||||
_utlConcept Assignable = | |||||
#if CXX_CONCEPTS | |||||
std::is_lvalue_reference<LHS>::value && | |||||
// CommonReference< | |||||
// const std::remove_reference_t<L>&, | |||||
// const std::remove_reference_t<R>&> && | |||||
requires(LHS lhs, RHS&& rhs) { | |||||
lhs = std::forward<RHS>(rhs); | |||||
requires Same< | |||||
decltype(lhs = std::forward<RHS>(rhs)), LHS | |||||
>; | |||||
}; | |||||
#else | |||||
std::is_assignable<LHS, RHS>::value; | |||||
#endif | |||||
/*! | |||||
* Swappable, SwappableWith | |||||
*/ | |||||
//! @{ | |||||
#if CXX_VER < CXX_VER_STD_17 | |||||
namespace swappable_with_impl { | |||||
struct is_swappable_with_ { | |||||
// can apply std::swap | |||||
template<typename _Tp, | |||||
typename _Up, | |||||
typename | |||||
= decltype(std::swap(std::declval<_Tp&>(), std::declval<_Up&>())), | |||||
typename | |||||
= decltype(std::swap(std::declval<_Up&>(), std::declval<_Tp&>()))> | |||||
static meta::true_ check(int); | |||||
// can not apply std::swap | |||||
template<typename, typename> static meta::false_ check(...); | |||||
}; | |||||
} | |||||
template <typename _Tp, typename _Up> | |||||
struct is_swappable_with | |||||
: swappable_with_impl::is_swappable_with_ { | |||||
using type = decltype(check<_Tp, _Up>(0)); | |||||
}; | |||||
#else | |||||
using is_swappable = std::is_swappable; | |||||
using is_swappable_with = std::is_swappable_with; | |||||
#endif | |||||
// != std:: on CommonReference | |||||
template<class T, class U> | |||||
_utlConcept SwappableWith = | |||||
is_swappable_with<T, T>::type::value && | |||||
is_swappable_with<U, U>::type::value && | |||||
is_swappable_with<T, U>::type::value && | |||||
is_swappable_with<U, T>::type::value; | |||||
// std::CommonReference< | |||||
// const std::remove_reference_t<T>&, | |||||
// const std::remove_reference_t<U>& | |||||
// >; | |||||
// != std:: we use is_swappable_with now is_swappable | |||||
template<class T> | |||||
_utlConcept Swappable = is_swappable_with<T, T>::type::value; | |||||
//! @} | |||||
/*! | |||||
* Destructible | |||||
*/ | |||||
template <class T> | |||||
_utlConcept Destructible = std::is_nothrow_destructible<T>::value; | |||||
/*! | |||||
* Constructible | |||||
*/ | |||||
template <class T, class... Args> | |||||
_utlConcept Constructible = | |||||
Destructible<T> && std::is_constructible<T, Args...>::value; | |||||
/*! | |||||
* DefaultConstructible | |||||
*/ | |||||
template <class T> | |||||
_utlConcept DefaultConstructible = Constructible<T>; | |||||
/*! | |||||
* MoveConstructible | |||||
* \note | |||||
* Another approach would be std::is_move_constructible<T>::value; | |||||
*/ | |||||
template<class T> | |||||
_utlConcept MoveConstructible = | |||||
Constructible<T, T> && ConvertibleTo<T, T>; | |||||
/*! | |||||
* CopyConstructible | |||||
*/ | |||||
template <class T> | |||||
_utlConcept CopyConstructible = | |||||
MoveConstructible<T> && | |||||
Constructible<T, _ref_t<T>> && ConvertibleTo<_ref_t<T>, T> && | |||||
Constructible<T, const _ref_t<T>> && ConvertibleTo<const _ref_t<T>, T> && | |||||
Constructible<T, const T> && ConvertibleTo<const T, T>; | |||||
/*! | |||||
* Movable | |||||
*/ | |||||
template <class T> | |||||
_utlConcept Movable = | |||||
std::is_object<T>::value && | |||||
MoveConstructible<T> && | |||||
Assignable<_ref_t<T>, T> && | |||||
Swappable<T>; | |||||
/*! | |||||
* Copyable | |||||
*/ | |||||
template <class T> | |||||
_utlConcept Copyable = | |||||
CopyConstructible<T> && | |||||
Movable<T> && | |||||
Assignable<_ref_t<T>, const _ref_t<T>>; | |||||
/*! | |||||
* Boolean | |||||
*/ | |||||
#if CXX_CONCEPTS | |||||
template <class B> | |||||
_utlConcept Boolean = | |||||
Movable<remove_cvref_t<B>> && | |||||
requires(const std::remove_reference_t<B>& b1, | |||||
const std::remove_reference_t<B>& b2, const bool a) { | |||||
requires ConvertibleTo<const std::remove_reference_t<B>&, bool>; | |||||
!b1; requires ConvertibleTo<decltype(!b1), bool>; | |||||
b1 && a; requires Same<decltype(b1 && a), bool>; | |||||
b1 || a; requires Same<decltype(b1 || a), bool>; | |||||
b1 && b2; requires Same<decltype(b1 && b2), bool>; | |||||
a && b2; requires Same<decltype(a && b2), bool>; | |||||
b1 || b2; requires Same<decltype(b1 || b2), bool>; | |||||
a || b2; requires Same<decltype(a || b2), bool>; | |||||
b1 == b2; requires ConvertibleTo<decltype(b1 == b2), bool>; | |||||
b1 == a; requires ConvertibleTo<decltype(b1 == a), bool>; | |||||
a == b2; requires ConvertibleTo<decltype(a == b2), bool>; | |||||
b1 != b2; requires ConvertibleTo<decltype(b1 != b2), bool>; | |||||
b1 != a; requires ConvertibleTo<decltype(b1 != a), bool>; | |||||
a != b2; requires ConvertibleTo<decltype(a != b2), bool>; | |||||
}; | |||||
#else | |||||
namespace details { | |||||
template <typename B, typename = void> | |||||
struct is_boolean_ { | |||||
using type = meta::false_; | |||||
}; | |||||
template <typename B> | |||||
struct is_boolean_ <B, meta::void_t< | |||||
meta::use_if_same_t<bool, decltype(!std::declval<cref_<B>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<B>>() == std::declval<cref_<B>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<B>>() != std::declval<cref_<B>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<B>>() && std::declval<cref_<B>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<B>>() || std::declval<cref_<B>>())> | |||||
>> { | |||||
using type = meta::true_; | |||||
}; | |||||
template <typename B> | |||||
using is_boolean_t = meta::eval < | |||||
is_boolean_<B> | |||||
>; | |||||
} | |||||
template <class B> | |||||
_utlConcept Boolean = | |||||
Movable<remove_cvref_t<B>> && | |||||
//ConvertibleTo<const std::remove_reference_t<B>&, bool> && | |||||
ConvertibleTo<const _ref_t<B>, bool> && | |||||
Same<meta::true_, details::is_boolean_t<B>>; | |||||
#endif | |||||
namespace details { | |||||
template <typename T, typename U, typename = void> | |||||
struct is_weakly_equality_comparable_with_ { | |||||
using type = meta::false_; | |||||
}; | |||||
template <typename T, typename U> | |||||
struct is_weakly_equality_comparable_with_<T, U, meta::void_t< | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() == std::declval<cref_<U>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() != std::declval<cref_<U>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<U>>() == std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<U>>() != std::declval<cref_<T>>())> | |||||
>> { | |||||
using type = meta::true_; | |||||
}; | |||||
template <typename T, typename U> | |||||
using is_weakly_equality_comparable_with_t = meta::eval< | |||||
is_weakly_equality_comparable_with_ <T, U> | |||||
>; | |||||
} | |||||
template <class T, class U> | |||||
_utlConcept WeaklyEqualityComparableWith = | |||||
#if CXX_CONCEPTS | |||||
requires(const std::remove_reference_t<T>& t, | |||||
const std::remove_reference_t<U>& u) { | |||||
t == u; requires Boolean<decltype(t == u)>; | |||||
t != u; requires Boolean<decltype(t != u)>; | |||||
u == t; requires Boolean<decltype(u == t)>; | |||||
u != t; requires Boolean<decltype(u != t)>; | |||||
}; | |||||
#else | |||||
Same<meta::true_, details::is_weakly_equality_comparable_with_t<T, U>>; | |||||
#endif | |||||
template <class T> | |||||
_utlConcept EqualityComparable = WeaklyEqualityComparableWith<T, T>; | |||||
template <class T, class U> | |||||
_utlConcept EqualityComparableWith = | |||||
EqualityComparable<T> && | |||||
EqualityComparable<U> && | |||||
// CommonReference< | |||||
// const std::remove_reference_t<T>&, | |||||
// const std::remove_reference_t<U>&> && | |||||
// EqualityComparable< | |||||
// common_reference_t< | |||||
// const std::remove_reference_t<T>&, | |||||
// const std::remove_reference_t<U>&>> && | |||||
WeaklyEqualityComparableWith<T, U>; | |||||
#if CXX_CONCEPTS | |||||
template <class T> | |||||
_utlConcept StrictTotallyOrdered = | |||||
EqualityComparable<T> && | |||||
requires(const std::remove_reference_t<T>& a, | |||||
const std::remove_reference_t<T>& b) { | |||||
a < b; requires Boolean<decltype(a < b)>; | |||||
a > b; requires Boolean<decltype(a > b)>; | |||||
a <= b; requires Boolean<decltype(a <= b)>; | |||||
a >= b; requires Boolean<decltype(a >= b)>; | |||||
}; | |||||
#else | |||||
namespace details { | |||||
template <typename T, typename = void> | |||||
struct is_strict_totally_ordered_ { | |||||
using type = meta::false_; | |||||
}; | |||||
template <typename T> | |||||
struct is_strict_totally_ordered_ <T, meta::void_t < | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() < std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() > std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() <= std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() >= std::declval<cref_<T>>())> | |||||
>> { | |||||
using type = meta::true_; | |||||
}; | |||||
template <typename T> | |||||
using is_strict_totally_ordered_t = meta::eval < | |||||
is_strict_totally_ordered_<T> | |||||
>; | |||||
} | |||||
template <class T> | |||||
_utlConcept StrictTotallyOrdered = | |||||
EqualityComparable<T> && | |||||
Same <meta::true_, details::is_strict_totally_ordered_t<T>>; | |||||
#endif | |||||
#if CXX_CONCEPTS | |||||
template <class T, class U> | |||||
_utlConcept StrictTotallyOrderedWith = | |||||
StrictTotallyOrdered<T> && | |||||
StrictTotallyOrdered<U> && | |||||
// CommonReference< | |||||
// const std::remove_reference_t<T>&, | |||||
// const std::remove_reference_t<U>& | |||||
// > && | |||||
// StrictTotallyOrdered< | |||||
// common_reference_t< | |||||
// const std::remove_reference_t<T>&, | |||||
// const std::remove_reference_t<U>& | |||||
// > | |||||
// > && | |||||
EqualityComparableWith<T, U> && | |||||
requires(const std::remove_reference_t<T>& t, | |||||
const std::remove_reference_t<U>& u) { | |||||
t < u; requires Boolean<decltype(t < u)>; | |||||
t > u; requires Boolean<decltype(t > u)>; | |||||
t <= u; requires Boolean<decltype(t <= u)>; | |||||
t >= u; requires Boolean<decltype(t >= u)>; | |||||
u < t; requires Boolean<decltype(u < t)>; | |||||
u > t; requires Boolean<decltype(u > t)>; | |||||
u <= t; requires Boolean<decltype(u <= t)>; | |||||
u >= t; requires Boolean<decltype(u >= t)>; | |||||
}; | |||||
#else | |||||
namespace details { | |||||
template <typename T, typename U, typename = void> | |||||
struct is_strict_totally_ordered_with_ { | |||||
using type = meta::false_; | |||||
}; | |||||
template <typename T, typename U> | |||||
struct is_strict_totally_ordered_with_ <T, U, meta::void_t < | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() < std::declval<cref_<U>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() > std::declval<cref_<U>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() <= std::declval<cref_<U>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<T>>() >= std::declval<cref_<U>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<U>>() < std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<U>>() > std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<U>>() <= std::declval<cref_<T>>())>, | |||||
meta::use_if_same_t<bool, decltype(std::declval<cref_<U>>() >= std::declval<cref_<T>>())> | |||||
>> { | |||||
using type = meta::true_; | |||||
}; | |||||
template <typename T, typename U> | |||||
using is_strict_totally_ordered_with_t = meta::eval < | |||||
is_strict_totally_ordered_with_<T, U> | |||||
>; | |||||
} | |||||
template <class T, class U> | |||||
_utlConcept StrictTotallyOrderedWith = | |||||
StrictTotallyOrdered<T> && | |||||
StrictTotallyOrdered<U> && | |||||
EqualityComparableWith<T, U> && | |||||
Same <meta::true_, details::is_strict_totally_ordered_with_t<T, U>>; | |||||
#endif | |||||
/*! | |||||
* Semiregular | |||||
*/ | |||||
template <class T> | |||||
_utlConcept Semiregular = Copyable<T> && DefaultConstructible<T>; | |||||
/*! | |||||
* Regular | |||||
*/ | |||||
template <class T> | |||||
_utlConcept Regular = Semiregular<T> && EqualityComparable<T>; | |||||
/*! | |||||
* Scalar | |||||
*/ | |||||
template<class T> | |||||
_utlConcept Scalar = | |||||
std::is_scalar<T>::value && Regular<T>; | |||||
/*! | |||||
* Arithmetic | |||||
*/ | |||||
template<class T> | |||||
_utlConcept Arithmetic = | |||||
std::is_arithmetic<T>::value && Scalar<T> && StrictTotallyOrdered<T>; | |||||
/*! | |||||
* FloatingPoint | |||||
*/ | |||||
template<class T> | |||||
_utlConcept FloatingPoint = | |||||
std::is_floating_point<T>::value && Arithmetic<T>; | |||||
/*! | |||||
* Invocable | |||||
*/ | |||||
template <class F, class... Args> | |||||
_utlConcept Invocable = is_invocable<F, Args...>::value; | |||||
// requires(F&& f, Args&&... args) { | |||||
// invoke(std::forward<F>(f), std::forward<Args>(args)...); | |||||
// }; | |||||
template< class F, class... Args > | |||||
_utlConcept RegularInvocable = Invocable<F, Args...>; | |||||
template < class F, class... Args > | |||||
_utlConcept Predicate = | |||||
RegularInvocable<F, Args...> && | |||||
Boolean<invoke_result_t<F, Args...>>; | |||||
template <class R, class T, class U> | |||||
_utlConcept Relation = | |||||
Predicate<R, T, T> && Predicate<R, U, U> && | |||||
Predicate<R, T, U> && Predicate<R, U, T>; | |||||
template < class R, class T, class U > | |||||
_utlConcept StrictWeakOrder = Relation<R, T, U>; | |||||
} | |||||
//! @} | |||||
#endif /* __utl_concepts_stl_h__ */ |
@@ -0,0 +1,187 @@ | |||||
/*! | |||||
* \file utl/utility/invoke.h | |||||
* \brief invoke() and invoke() traits implementation | |||||
* | |||||
* Copyright (C) 2018-2019 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more detail. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef __utl_utility_invoke_h__ | |||||
#define __utl_utility_invoke_h__ | |||||
#include <utl/core/impl.h> | |||||
#include <utl/meta/meta.h> | |||||
#include <type_traits> | |||||
#include <functional> | |||||
#include <utility> | |||||
/*! | |||||
* \ingroup utility | |||||
* \defgroup invoke | |||||
* | |||||
*/ | |||||
//! @{ | |||||
namespace utl { | |||||
namespace detail { | |||||
template <class T> | |||||
struct is_ref_wrapper : meta::false_ {}; | |||||
template <class U> | |||||
struct is_ref_wrapper<std::reference_wrapper<U>> : meta::true_ {}; | |||||
// 1 | |||||
template <class T, class Type, class T1, class... Args, | |||||
meta::enable_if_t< | |||||
std::is_member_function_pointer<std::decay_t<Type T::*>>::value && | |||||
std::is_base_of<T, std::decay_t<T1>>::value, | |||||
int> =0 | |||||
> | |||||
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) { | |||||
return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...); | |||||
} | |||||
// 2 | |||||
template <class T, class Type, class T1, class... Args, | |||||
meta::enable_if_t< | |||||
std::is_member_function_pointer<std::decay_t<Type T::*>>::value && | |||||
is_ref_wrapper<std::decay_t<T1>>::value, | |||||
int> =0 | |||||
> | |||||
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) { | |||||
return (t1.get().*f)(std::forward<Args>(args)...); | |||||
} | |||||
// 3 | |||||
template <class T, class Type, class T1, class... Args, | |||||
meta::enable_if_t< | |||||
std::is_member_function_pointer<std::decay_t<Type T::*>>::value && | |||||
!std::is_base_of<T, std::decay_t<T1>>::value && | |||||
!is_ref_wrapper<std::decay_t<T1>>::value, | |||||
int> =0 | |||||
> | |||||
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) { | |||||
return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...); | |||||
} | |||||
// 4 | |||||
template <class T, class Type, class T1, class... Args, | |||||
meta::enable_if_t< | |||||
std::is_member_object_pointer<std::decay_t<Type T::*>>::value && | |||||
std::is_base_of<T, std::decay_t<T1>>::value, | |||||
int> =0 | |||||
> | |||||
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) { | |||||
return std::forward<T1>(t1).*f; | |||||
} | |||||
// 5 | |||||
template <class T, class Type, class T1, class... Args, | |||||
meta::enable_if_t< | |||||
std::is_member_object_pointer<std::decay_t<Type T::*>>::value && | |||||
is_ref_wrapper<std::decay_t<T1>>::value, | |||||
int> =0 | |||||
> | |||||
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) { | |||||
return t1.get().*f; | |||||
} | |||||
// 6 | |||||
template <class T, class Type, class T1, class... Args, | |||||
meta::enable_if_t< | |||||
std::is_member_object_pointer<std::decay_t<Type T::*>>::value && | |||||
!std::is_base_of<T, std::decay_t<T1>>::value && | |||||
!is_ref_wrapper<std::decay_t<T1>>::value, | |||||
int> =0 | |||||
> | |||||
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) { | |||||
return (*std::forward<T1>(t1)).*f; | |||||
} | |||||
template <class F, class... Args> | |||||
decltype(auto) invoke_impl_(F&& f, Args&&... args) { | |||||
return std::forward<F>(f)(std::forward<Args>(args)...); | |||||
} | |||||
} // namespace detail | |||||
//! Invoke a callable object (for C++14) | |||||
template<typename _Callable, typename... _Args> | |||||
inline decltype(auto) invoke(_Callable&& fn, _Args&&... args) { | |||||
return detail::invoke_impl_( | |||||
std::forward<_Callable>(fn), std::forward<_Args>(args)... | |||||
); | |||||
} | |||||
//! @} | |||||
//! std::is_invocable trait for C++11 | |||||
template <typename F, typename... Args> | |||||
struct is_invocable : | |||||
std::is_constructible< | |||||
std::function<void(Args ...)>, | |||||
std::reference_wrapper<typename std::remove_reference<F>::type> | |||||
> { }; | |||||
//! std::is_invocable_r trait for C++11 | |||||
template <typename R, typename F, typename... Args> | |||||
struct is_invocable_r : | |||||
std::is_constructible< | |||||
std::function<R(Args ...)>, | |||||
std::reference_wrapper<typename std::remove_reference<F>::type> | |||||
> { }; | |||||
/*! | |||||
* invoke_result (SFINAE friendly) | |||||
*/ | |||||
//! @{ | |||||
namespace detail { | |||||
template<typename _Callable, typename... _Args> | |||||
struct try_invoke { | |||||
using type = decltype ( | |||||
detail::invoke_impl_(std::declval<_Callable&&>(), std::declval<_Args&&>()...) | |||||
); | |||||
}; | |||||
template<bool B, typename _Callable, typename... _Args> | |||||
struct invoke_result_ { | |||||
using type = meta::nil_; | |||||
}; | |||||
template <typename _Callable, typename... _Args> | |||||
struct invoke_result_ <true, _Callable, _Args...> { | |||||
using type = meta::invoke_t< | |||||
meta::quote<try_invoke>, _Callable, _Args... | |||||
>; | |||||
}; | |||||
} | |||||
//! invoke_result (for C++14) | |||||
template <typename _Callable, typename... _Args> | |||||
using invoke_result = detail::invoke_result_< | |||||
is_invocable<_Callable, _Args...>::value, | |||||
_Callable, | |||||
_Args... | |||||
>; | |||||
//! invoke_result_t (for C++14) | |||||
template<typename _Callable, typename... _Args> | |||||
using invoke_result_t = meta::eval < | |||||
invoke_result<_Callable, _Args...> | |||||
>; | |||||
} | |||||
//! @} | |||||
#endif /* __utl_utility_invoke_h__ */ |
@@ -0,0 +1,422 @@ | |||||
/*! | |||||
* \file Tmeta.cpp | |||||
* | |||||
* Copyright (C) 2018 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#include <utl/concepts/concepts.h> | |||||
#include <utl/meta/meta.h> | |||||
#include <gtest/gtest.h> | |||||
namespace test_concepts { | |||||
using namespace utl; | |||||
/* | |||||
* Fixture like types | |||||
*/ | |||||
struct Empty { }; | |||||
struct HaveOnlyCopy { | |||||
HaveOnlyCopy(const HaveOnlyCopy&) = default; | |||||
HaveOnlyCopy(HaveOnlyCopy&&) = delete; | |||||
HaveOnlyCopy& operator= (const HaveOnlyCopy&) = default; | |||||
HaveOnlyCopy& operator= (HaveOnlyCopy&&) = delete; | |||||
}; | |||||
struct HaveOnlyMove { | |||||
HaveOnlyMove(const HaveOnlyMove&) = delete; | |||||
HaveOnlyMove(HaveOnlyMove&&) = default; | |||||
HaveOnlyMove& operator= (const HaveOnlyMove&) = delete; | |||||
HaveOnlyMove& operator= (HaveOnlyMove&&) = default; | |||||
}; | |||||
struct HaveCopyAndMove { | |||||
HaveCopyAndMove(const HaveCopyAndMove&) = default; | |||||
HaveCopyAndMove(HaveCopyAndMove&&) = default; | |||||
HaveCopyAndMove& operator= (const HaveCopyAndMove&) = default; | |||||
HaveCopyAndMove& operator= (HaveCopyAndMove&&) = default; | |||||
}; | |||||
class HavePerfectForwarding { | |||||
public: template<class T> HavePerfectForwarding(T&&) { } | |||||
}; | |||||
class Base { }; | |||||
class Derived1 : public Base { }; | |||||
class Derived2 : public Derived1 { }; | |||||
class HaveOperatorBase { | |||||
public: operator Base() { return base; } | |||||
Base base; | |||||
}; | |||||
struct A { | |||||
int a_; | |||||
public: | |||||
A(int a =0) : a_{a} { }; | |||||
A(const A&) = default; | |||||
A(A&&) = default; | |||||
}; | |||||
bool operator== (const A& l, const A& r) { return l.a_ == r.a_; } | |||||
bool operator!= (const A& l, const A& r) { return l.a_ != r.a_; } | |||||
struct B { | |||||
int b_; | |||||
public: | |||||
B(int b =0) : b_{b} { }; | |||||
B(const B&) = default; | |||||
B(B&&) = default; | |||||
B& operator= (const B&) = default; | |||||
B& operator= (B&&) = default; | |||||
}; | |||||
bool operator== (const B& l, const B& r) { return l.b_ == r.b_; } | |||||
bool operator!= (const B& l, const B& r) { return l.b_ != r.b_; } | |||||
bool operator< (const B& l, const B& r) { return l.b_ < r.b_; } | |||||
bool operator<= (const B& l, const B& r) { return l.b_ <= r.b_; } | |||||
bool operator> (const B& l, const B& r) { return l.b_ > r.b_; } | |||||
bool operator>= (const B& l, const B& r) { return l.b_ >= r.b_; } | |||||
TEST(TConcepts, Same) { | |||||
// Same | |||||
EXPECT_EQ (true, (Same<int, int>)); | |||||
EXPECT_EQ (false, (Same<int, long>)); | |||||
EXPECT_EQ (true, (Same<int*, int*>)); | |||||
EXPECT_EQ (true, (Same<double&, double&>)); | |||||
EXPECT_EQ (false, (Same<int, Empty>)); | |||||
EXPECT_EQ (false, (Same<Base, Derived1>)); | |||||
} | |||||
TEST(TConcepts, DerivedFrom) { | |||||
// DerivedFrom | |||||
EXPECT_EQ (true, (DerivedFrom<Derived1, Base>)); | |||||
EXPECT_EQ (true, (DerivedFrom<Derived2, Derived1>)); | |||||
EXPECT_EQ (true, (DerivedFrom<Derived2, Base>)); | |||||
EXPECT_EQ (false, (DerivedFrom<Base, Derived1>)); | |||||
EXPECT_EQ (false, (DerivedFrom<Base, int>)); | |||||
EXPECT_EQ (false, (DerivedFrom<void, int>)); | |||||
} | |||||
TEST(TConcepts, ConvertibleTo) { | |||||
// ConvertibleTo | |||||
EXPECT_EQ (true, (ConvertibleTo<void, void>)); | |||||
EXPECT_EQ (false, (ConvertibleTo<Base, void>)); | |||||
EXPECT_EQ (false, (ConvertibleTo<Base*, Derived1*>)); | |||||
EXPECT_EQ (true, (ConvertibleTo<Derived1*, Base*>)); | |||||
EXPECT_EQ (true, (ConvertibleTo<HaveOperatorBase, Base>)); | |||||
EXPECT_EQ (true, (ConvertibleTo<Base, HavePerfectForwarding>)); | |||||
} | |||||
TEST(TConcepts, CommonReference) { | |||||
// CommonReference | |||||
EXPECT_EQ (true, (CommonReference<Derived1, Base>)); | |||||
EXPECT_EQ (true, (CommonReference<Derived1&, Base>)); | |||||
EXPECT_EQ (true, (CommonReference<const Empty&&, const Empty&>)); | |||||
EXPECT_EQ (false, (CommonReference<Empty&, const volatile Empty&>)); | |||||
//FIXME: CommonReference needs SFINAE friendly implementation | |||||
//EXPECT_EQ (false, (CommonReference<Empty&&, const volatile Empty&>)); <- yields compiler error | |||||
// Common | |||||
EXPECT_EQ (true, (Common<int, int>)); | |||||
EXPECT_EQ (true, (Common<Base, Derived1>)); | |||||
EXPECT_EQ (true, (Common<Derived1, Derived2>)); | |||||
EXPECT_EQ (true, (Common<Base, HaveOperatorBase>)); | |||||
EXPECT_EQ (true, (Common<Base, HavePerfectForwarding>)); | |||||
} | |||||
TEST(TConcepts, Integral) { | |||||
// Integral | |||||
EXPECT_EQ (false, (Integral<void>)); | |||||
EXPECT_EQ (true, (Integral<int>)); | |||||
EXPECT_EQ (true, (Integral<bool>)); | |||||
EXPECT_EQ (false, (Integral<int*>)); | |||||
EXPECT_EQ (false, (Integral<Base>)); | |||||
EXPECT_EQ (true, (Integral<meta::int_<42>::value_type>)); | |||||
EXPECT_EQ (false, (Integral<meta::int_<42>::type>)); | |||||
// SignedIntegral | |||||
EXPECT_EQ (false, (SignedIntegral<void>)); | |||||
EXPECT_EQ (true, (SignedIntegral<int>)); | |||||
EXPECT_EQ (false, (SignedIntegral<int*>)); | |||||
EXPECT_EQ (false, (SignedIntegral<unsigned long>)); | |||||
EXPECT_EQ (false, (SignedIntegral<double>)); | |||||
EXPECT_EQ (false, (SignedIntegral<Base>)); | |||||
EXPECT_EQ (true, (SignedIntegral<meta::int16_<42>::value_type>)); | |||||
// UnsignedIntegral | |||||
EXPECT_EQ (false, (UnsignedIntegral<void>)); | |||||
EXPECT_EQ (true, (UnsignedIntegral<unsigned int>)); | |||||
EXPECT_EQ (false, (UnsignedIntegral<long>)); | |||||
EXPECT_EQ (false, (UnsignedIntegral<double>)); | |||||
EXPECT_EQ (false, (UnsignedIntegral<Base>)); | |||||
EXPECT_EQ (true, (UnsignedIntegral<meta::uint16_<42>::value_type>)); | |||||
} | |||||
TEST(TConcepts, Assignable) { | |||||
// MoveAssignable | |||||
EXPECT_EQ (false, (MoveAssignable<void>)); | |||||
EXPECT_EQ (true, (MoveAssignable<void*>)); | |||||
EXPECT_EQ (true, (MoveAssignable<int>)); | |||||
EXPECT_EQ (true, (MoveAssignable<int*>)); | |||||
EXPECT_EQ (false, (MoveAssignable<HaveOnlyCopy>)); | |||||
EXPECT_EQ (true, (MoveAssignable<HaveOnlyMove>)); | |||||
EXPECT_EQ (true, (MoveAssignable<HaveCopyAndMove>)); | |||||
EXPECT_EQ (true, (MoveAssignable<Empty>)); | |||||
EXPECT_EQ (true, (MoveAssignable<HavePerfectForwarding>)); | |||||
// CopyAssignable | |||||
EXPECT_EQ (false, (CopyAssignable<void>)); | |||||
EXPECT_EQ (true, (CopyAssignable<void*>)); | |||||
EXPECT_EQ (true, (CopyAssignable<int>)); | |||||
EXPECT_EQ (true, (CopyAssignable<int*>)); | |||||
EXPECT_EQ (true, (CopyAssignable<HaveOnlyCopy>)); | |||||
EXPECT_EQ (false, (CopyAssignable<HaveOnlyMove>)); | |||||
EXPECT_EQ (true, (CopyAssignable<HaveCopyAndMove>)); | |||||
EXPECT_EQ (true, (CopyAssignable<Empty>)); | |||||
EXPECT_EQ (true, (CopyAssignable<HavePerfectForwarding>)); | |||||
// Assignable | |||||
EXPECT_EQ (false, (Assignable<void, void>)); | |||||
EXPECT_EQ (false, (Assignable<int&, void>)); | |||||
EXPECT_EQ (true, (Assignable<int&, int>)); | |||||
EXPECT_EQ (false, (Assignable<int, int>)); | |||||
EXPECT_EQ (false, (Assignable<int*, int*>)); | |||||
EXPECT_EQ (true, (Assignable<Base&, Derived1>)); | |||||
EXPECT_EQ (false, (Assignable<Derived1&, Base>)); | |||||
EXPECT_EQ (true, (Assignable<HaveOnlyMove&, HaveOnlyMove&&>)); | |||||
EXPECT_EQ (true , (Assignable<HaveOnlyMove&, HaveOnlyMove>)); | |||||
EXPECT_EQ (true, (Assignable<Empty&, Empty>)); | |||||
} | |||||
TEST(TConcepts, Swappable) { | |||||
// Swappable, SwappableWith | |||||
EXPECT_EQ (false, (Swappable<void>)); | |||||
EXPECT_EQ (true, (Swappable<void*>)); | |||||
EXPECT_EQ (true, (Swappable<int>)); | |||||
EXPECT_EQ (true, (Swappable<Base>)); | |||||
EXPECT_EQ (true, (SwappableWith<int, int>)); | |||||
EXPECT_EQ (false, (SwappableWith<int, Base>)); | |||||
EXPECT_EQ (false, (SwappableWith<Base, Derived1>)); | |||||
// Destructible | |||||
EXPECT_EQ (false, (Destructible<void>)); | |||||
EXPECT_EQ (true, (Destructible<void*>)); | |||||
EXPECT_EQ (true, (Destructible<int>)); | |||||
EXPECT_EQ (true, (Destructible<int&>)); | |||||
EXPECT_EQ (true, (Destructible<Base>)); | |||||
EXPECT_EQ (true, (Destructible<HavePerfectForwarding>)); | |||||
} | |||||
TEST(TConcepts, Constructible) { | |||||
// Constructible | |||||
EXPECT_EQ (false, (Constructible<void>)); | |||||
EXPECT_EQ (true, (Constructible<void*>)); | |||||
EXPECT_EQ (true, (Constructible<Base>)); | |||||
EXPECT_EQ (false, (Constructible<HaveOnlyMove>)); | |||||
EXPECT_EQ (true, (Constructible<HavePerfectForwarding, int>)); | |||||
// DefaultConstructible | |||||
EXPECT_EQ (false, (DefaultConstructible<void>)); | |||||
EXPECT_EQ (true, (DefaultConstructible<void*>)); | |||||
EXPECT_EQ (false, (DefaultConstructible<int&>)); | |||||
EXPECT_EQ (true, (DefaultConstructible<Base>)); | |||||
EXPECT_EQ (true, (DefaultConstructible<Derived1>)); | |||||
EXPECT_EQ (false, (DefaultConstructible<HaveOnlyCopy>)); | |||||
EXPECT_EQ (false, (DefaultConstructible<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (DefaultConstructible<HavePerfectForwarding>)); | |||||
// MoveConstructible | |||||
EXPECT_EQ (false, (MoveConstructible<void>)); | |||||
EXPECT_EQ (true, (MoveConstructible<void*>)); | |||||
EXPECT_EQ (true, (MoveConstructible<Base>)); | |||||
EXPECT_EQ (true, (MoveConstructible<Derived1>)); | |||||
EXPECT_EQ (true, (MoveConstructible<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (MoveConstructible<HaveOnlyCopy>)); | |||||
EXPECT_EQ (true, (MoveConstructible<HaveCopyAndMove>)); | |||||
EXPECT_EQ (true , (MoveConstructible<HavePerfectForwarding>)); | |||||
// CopyConstructible | |||||
EXPECT_EQ (false, (CopyConstructible<void>)); | |||||
EXPECT_EQ (true, (CopyConstructible<void*>)); | |||||
EXPECT_EQ (true, (CopyConstructible<Base>)); | |||||
EXPECT_EQ (true, (CopyConstructible<Derived1>)); | |||||
EXPECT_EQ (false, (CopyConstructible<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (CopyConstructible<HaveOnlyCopy>)); | |||||
EXPECT_EQ (true, (CopyConstructible<HaveCopyAndMove>)); | |||||
EXPECT_EQ (true , (CopyConstructible<HavePerfectForwarding>)); | |||||
} | |||||
TEST(TConcepts, MovableCopyable) { | |||||
// Movable | |||||
EXPECT_EQ (false, (Movable<void>)); | |||||
EXPECT_EQ (true, (Movable<int>)); | |||||
EXPECT_EQ (true, (Movable<Base>)); | |||||
EXPECT_EQ (true, (Movable<Derived1>)); | |||||
EXPECT_EQ (true, (Movable<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (Movable<HaveOnlyCopy>)); | |||||
EXPECT_EQ (true, (Movable<HaveCopyAndMove>)); | |||||
EXPECT_EQ (true , (Movable<HavePerfectForwarding>)); | |||||
// Copyable | |||||
EXPECT_EQ (false, (Copyable<void>)); | |||||
EXPECT_EQ (true, (Copyable<int>)); | |||||
EXPECT_EQ (true, (Copyable<Base>)); | |||||
EXPECT_EQ (true, (Copyable<Derived1>)); | |||||
EXPECT_EQ (false, (Copyable<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (Copyable<HaveOnlyCopy>)); | |||||
EXPECT_EQ (true, (Copyable<HaveCopyAndMove>)); | |||||
EXPECT_EQ (true , (Copyable<HavePerfectForwarding>)); | |||||
} | |||||
TEST(TConcepts, Boolean) { | |||||
// Boolean | |||||
EXPECT_EQ (false, (Boolean<void>)); | |||||
EXPECT_EQ (true, (Boolean<bool>)); | |||||
EXPECT_EQ (true, (Boolean<int>)); | |||||
EXPECT_EQ (true, (Boolean<double>)); | |||||
EXPECT_EQ (true, (Boolean<std::true_type>)); | |||||
EXPECT_EQ (true, (Boolean<meta::true_>)); | |||||
EXPECT_EQ (false, (Boolean<Empty>)); | |||||
} | |||||
TEST(TConcepts, Comparable) { | |||||
// EqualityComparable | |||||
EXPECT_EQ (false, (EqualityComparable<void>)); | |||||
EXPECT_EQ (true, (EqualityComparable<bool>)); | |||||
EXPECT_EQ (true, (EqualityComparable<int>)); | |||||
EXPECT_EQ (false, (EqualityComparable<Empty>)); | |||||
EXPECT_EQ (true, (EqualityComparable<A>)); | |||||
// EqualityComparableWith | |||||
EXPECT_EQ (false, (EqualityComparableWith<void, bool>)); | |||||
EXPECT_EQ (false, (EqualityComparableWith<void, void>)); | |||||
EXPECT_EQ (true, (EqualityComparableWith<bool, bool>)); | |||||
EXPECT_EQ (true, (EqualityComparableWith<int, int>)); | |||||
EXPECT_EQ (true, (EqualityComparableWith<int, bool>)); | |||||
EXPECT_EQ (false, (EqualityComparableWith<Empty, Empty>)); | |||||
EXPECT_EQ (false, (EqualityComparableWith<int, Empty>)); | |||||
EXPECT_EQ (true, (EqualityComparableWith<A, A>)); | |||||
EXPECT_EQ (false, (EqualityComparableWith<A, B>)); | |||||
// StrictTotallyOrdered | |||||
EXPECT_EQ (false, (StrictTotallyOrdered<void>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrdered<bool>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrdered<int>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrdered<double>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrdered<Empty>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrdered<A>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrdered<B>)); | |||||
// StrictTotallyOrderedWith | |||||
EXPECT_EQ (false, (StrictTotallyOrderedWith<void, void>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrderedWith<int, void>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrderedWith<bool, bool>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrderedWith<int, double>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrderedWith<int, Empty>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrderedWith<Base, Derived1>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrderedWith<A, A>)); | |||||
EXPECT_EQ (true, (StrictTotallyOrderedWith<B, B>)); | |||||
EXPECT_EQ (false, (StrictTotallyOrderedWith<A, B>)); | |||||
} | |||||
TEST(TConcepts, Types) { | |||||
// Semiregular | |||||
EXPECT_EQ (false, (Semiregular<void>)); | |||||
EXPECT_EQ (true, (Semiregular<int>)); | |||||
EXPECT_EQ (true, (Semiregular<Empty>)); | |||||
EXPECT_EQ (false, (Semiregular<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (Semiregular<HaveOnlyCopy>)); | |||||
EXPECT_EQ (false, (Semiregular<HaveCopyAndMove>)); | |||||
EXPECT_EQ (false, (Semiregular<A>)); | |||||
EXPECT_EQ (true, (Semiregular<B>)); | |||||
// Regular | |||||
EXPECT_EQ (false, (Regular<void>)); | |||||
EXPECT_EQ (true, (Regular<int>)); | |||||
EXPECT_EQ (true, (Regular<int*>)); | |||||
EXPECT_EQ (false, (Regular<Empty>)); | |||||
EXPECT_EQ (false, (Regular<HaveOnlyMove>)); | |||||
EXPECT_EQ (false, (Regular<HaveOnlyCopy>)); | |||||
EXPECT_EQ (false, (Regular<HaveCopyAndMove>)); | |||||
EXPECT_EQ (false, (Regular<A>)); | |||||
EXPECT_EQ (true, (Regular<B>)); | |||||
// Scalar | |||||
EXPECT_EQ (false, (Scalar<void>)); | |||||
EXPECT_EQ (true, (Scalar<int>)); | |||||
EXPECT_EQ (true, (Scalar<long*>)); | |||||
EXPECT_EQ (false, (Scalar<A>)); | |||||
EXPECT_EQ (false, (Scalar<B>)); | |||||
// Arithmetic | |||||
EXPECT_EQ (false, (Arithmetic<void>)); | |||||
EXPECT_EQ (true, (Arithmetic<int>)); | |||||
EXPECT_EQ (false, (Arithmetic<long*>)); | |||||
EXPECT_EQ (false, (Arithmetic<A>)); | |||||
EXPECT_EQ (false, (Arithmetic<B>)); | |||||
// FloatingPoint | |||||
EXPECT_EQ (false, (FloatingPoint<void>)); | |||||
EXPECT_EQ (true, (FloatingPoint<float>)); | |||||
EXPECT_EQ (true, (FloatingPoint<double>)); | |||||
EXPECT_EQ (false, (FloatingPoint<int>)); | |||||
EXPECT_EQ (false, (FloatingPoint<float*>)); | |||||
EXPECT_EQ (false, (FloatingPoint<A>)); | |||||
EXPECT_EQ (false, (FloatingPoint<B>)); | |||||
} | |||||
struct Inv { | |||||
void operator() (int) { }; | |||||
void operator() () { }; | |||||
}; | |||||
struct Pred { | |||||
bool operator () (int) { return true; } | |||||
bool operator () (int, int) { return true; } | |||||
}; | |||||
TEST(TConcepts, Callable) { | |||||
EXPECT_EQ (true, (Invocable<Inv, int>)); | |||||
EXPECT_EQ (true, (Invocable<Inv>)); | |||||
EXPECT_EQ (true, (Invocable<Inv, double>)); | |||||
EXPECT_EQ (false, (Invocable<Inv, Empty>)); | |||||
EXPECT_EQ (true, (RegularInvocable<Inv, int>)); | |||||
EXPECT_EQ (false, (Predicate<Inv, int>)); | |||||
EXPECT_EQ (true, (Predicate<Pred, int>)); | |||||
EXPECT_EQ (false, (Predicate<Pred, Empty>)); | |||||
EXPECT_EQ (true, (Relation<Pred, int, int>)); | |||||
EXPECT_EQ (true, (Relation<Pred, int, double>)); | |||||
EXPECT_EQ (false, (Relation<Pred, Empty, int>)); | |||||
EXPECT_EQ (true, (StrictWeakOrder<Pred, int, int>)); | |||||
EXPECT_EQ (false, (StrictWeakOrder<Pred, int, Empty>)); | |||||
} | |||||
struct Incr { | |||||
Incr& operator++() { return *this; } | |||||
Incr operator++(int) { return *this; } | |||||
}; | |||||
int type_printer (int* i) { return *i; } | |||||
TEST(TConcepts, Iterators) { | |||||
// type_printer(detail::try_ppI<Incr&>{}); | |||||
// type_printer(detail::try_Ipp<Incr&>{}); | |||||
// type_printer(meta::detected_t<detail::try_ppI, int>{}); | |||||
EXPECT_EQ (true, (WeaklyIncrementable<int>)); | |||||
EXPECT_EQ (false, (WeaklyIncrementable<void>)); | |||||
EXPECT_EQ (false, (WeaklyIncrementable<meta::nil_>)); | |||||
EXPECT_EQ (true, (WeaklyIncrementable<Incr>)); | |||||
EXPECT_EQ (false, (WeaklyIncrementable<Incr&>)); | |||||
} | |||||
} | |||||
@@ -0,0 +1,72 @@ | |||||
/*! | |||||
* \file Tinvoke.cpp | |||||
* | |||||
* Copyright (C) 2018-2019 Christos Choutouridis | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Lesser General Public License as | |||||
* published by the Free Software Foundation, either version 3 | |||||
* of the License, or (at your option) any later version. | |||||
* | |||||
* This program 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 Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#include <utl/utility/invoke.h> | |||||
#include <gtest/gtest.h> | |||||
#include <type_traits> | |||||
namespace test_meta { | |||||
using namespace utl; | |||||
/* | |||||
* Types to behave like Fixtures | |||||
*/ | |||||
int Ifun(int i) { return i; } | |||||
struct Ifoo { | |||||
int operator() (int i) { | |||||
return i; | |||||
} | |||||
}; | |||||
struct Ibar { | |||||
Ibar(int num) : num_(num) {} | |||||
int echo(int i) const { return i; } | |||||
int get() const { return num_; } | |||||
int num_; | |||||
}; | |||||
/* | |||||
* Test invoke() | |||||
*/ | |||||
TEST(Tinvoke, Invoke) { | |||||
Ifoo ifoo{}; | |||||
const Ibar ibar{42}; | |||||
EXPECT_EQ (42, invoke(Ifun, 42)); | |||||
EXPECT_EQ (42, invoke([] () { return Ifun(42); })); | |||||
EXPECT_EQ (42, invoke(Ifoo{}, 42)); | |||||
EXPECT_EQ (42, invoke(ifoo, 42)); | |||||
EXPECT_EQ (42, invoke(&Ibar::echo, ibar, 42)); | |||||
EXPECT_EQ (42, invoke(&Ibar::get, ibar)); | |||||
EXPECT_EQ (true, (is_invocable<Ifoo, int>::value)); | |||||
EXPECT_EQ (false, (is_invocable<Ifoo>::value)); | |||||
EXPECT_EQ (false, (is_invocable<Ifoo, int, double>::value)); | |||||
EXPECT_EQ (false, (is_invocable<Ibar, int>::value)); | |||||
EXPECT_EQ (true, (is_invocable_r<int, Ifoo, int>::value)); | |||||
EXPECT_EQ (false, (is_invocable_r<int*, Ifoo, int>::value)); | |||||
EXPECT_EQ (false, (is_invocable_r<int, Ifoo>::value)); | |||||
EXPECT_EQ (true, (std::is_same<int, invoke_result_t<Ifoo, int>>())); | |||||
EXPECT_EQ (true, (std::is_same<meta::nil_, invoke_result_t<Ifoo>>())); | |||||
EXPECT_EQ (true, (std::is_same<meta::nil_, invoke_result_t<Ifoo, int, double>>())); | |||||
} | |||||
} |