/*!
* \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 .
*
*/
#ifndef __utl_concepts_stl_h__
#define __utl_concepts_stl_h__
#include
#include
#include
#include
/*!
* \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
using remove_cvref_t = std::remove_cv_t< std::remove_reference_t >;
template
using cref_ = const std::remove_reference_t&;
template
using _ref_t = std::add_lvalue_reference_t;
template
using use_if_same_t = meta::eval<
meta::enable_if<
meta::same_<_T1, _T2>::value, _Ret
>
>;
/*!
* Same
*/
template
_utlConcept Same = meta::same_::value;
// template
// _utlConcept Decayed = Same>;
/*!
* DerivedFrom
*/
template
_utlConcept DerivedFrom =
std::is_base_of::value &&
std::is_convertible::value;
/*!
* ConvertibleTo
*/
template
#if CXX_CONCEPTS
_utlConcept ConvertibleTo =
std::is_convertible::value &&
requires(From (&f)()) {
static_cast(f());
};
#else
_utlConcept ConvertibleTo = std::is_convertible::value;
#endif
/*!
* Common Reference
*/
//! @{
namespace common_impl {
//! \see https://ericniebler.github.io/std/wg21/D0022.html
// ========== common reference ===========
template
using __cond_res =
decltype(false ? std::declval()() : std::declval()());
template
struct __copy_cv_ {
static_assert(!std::is_reference::value);
template using apply = To;
};
template
struct __copy_cv_ {
template using apply = const To;
};
template
struct __copy_cv_ {
template using apply = volatile To;
};
template
struct __copy_cv_ {
template using apply = const volatile To;
};
template
using __copy_cv = meta::invoke<__copy_cv_, To>;
// CREF [meta.trans.other]/2.1
template
using __cref = std::add_lvalue_reference_t>;
// COMMON_REF [meta.trans.other]/2
template
struct __common_ref_ {
static_assert(std::is_reference::value, "");
static_assert(std::is_reference::value, "");
};
template
using __common_ref = meta::eval<__common_ref_>;
// [meta.trans.other]/2.5
template
using __lref_res = __cond_res<
__copy_cv &,
__copy_cv &
>;
// [meta.trans.other]/2.6
template>
using __rref_res = std::remove_reference_t&&;
template
struct __common_ref_,
meta::when>::value>> > {
using type = __lref_res;
};
template
struct __common_ref_,
meta::when>>,
meta::when>>> > {
using type = __rref_res;
};
// [meta.trans.other]/2.7
template
struct __common_ref_,
meta::when>>> > {
using type = __common_ref;
};
// [meta.trans.other]/2.8
template
struct __common_ref_,
meta::when>>> > {
using type = __common_ref;
};
template
struct __xref {
template using apply = U;
};
template
struct __xref {
template using apply = const U;
};
template
struct __xref {
template using apply = volatile U;
};
template
struct __xref {
template using apply = const volatile U;
};
template
struct __xref {
template using apply =
std::add_lvalue_reference_t, U>>;
};
template
struct __xref {
template using apply =
std::add_rvalue_reference_t, U>>;
};
template class,
template class
>
struct basic_common_reference { };
template
using __basic_common_reference_t = meta::eval<
basic_common_reference<
remove_cvref_t,
remove_cvref_t,
__xref::template apply,
__xref::template apply
>
>;
template
struct common_reference {};
template
using common_reference_t = meta::eval<
common_reference
>;
// [meta.trans.other]/5.2
template
struct common_reference {
using type = T;
};
// [meta.trans.other]/5.3.4
template
struct __common_reference3
: std::common_type {};
// [meta.trans.other]/5.3.3
template
struct __common_reference3>> {
using type = __cond_res;
};
template
struct __common_reference2
: __common_reference3 {};
// [meta.trans.other]/5.3.2
template
struct __common_reference2>> {
using type = __basic_common_reference_t;
};
template
struct __common_reference
: __common_reference2 { };
template
struct __common_reference::value && std::is_reference::value>> {
using type = __common_ref;
};
template
struct common_reference : __common_reference { };
// [meta.trans.other]/5.4
template
//requires requires { typename common_reference_t; }
struct common_reference
: common_reference <
common_reference_t, V, W...
> {};
}
template
using common_reference = common_impl::common_reference;
template
using common_reference_t = meta::eval<
common_reference
>;
//! @}
//FIXME: CommonReference needs better implementation
template
_utlConcept CommonReference =
Same, common_reference_t> &&
ConvertibleTo> &&
ConvertibleTo>;
// != std::Common on CommonReference
template
_utlConcept Common =
#if CXX_CONCEPTS
Same, std::common_type_t> &&
requires {
static_cast>(std::declval());
static_cast>(std::declval());
};
// } &&
// CommonReference<
// std::add_lvalue_reference_t,
// std::add_lvalue_reference_t> &&
// CommonReference<
// std::add_lvalue_reference_t>,
// std::common_reference_t<
// std::add_lvalue_reference_t,
// std::add_lvalue_reference_t
// >
// >;
#else
// meta::and_ <
Same, std::common_type_t>; //>
// meta::bool_,
// std::add_lvalue_reference_t
// >>,
// meta::bool_< CommonReference<
// std::add_lvalue_reference_t>,
// common_reference_t<
// std::add_lvalue_reference_t,
// std::add_lvalue_reference_t
// >
// >>
// >::value;
#endif
/*!
* Integral
*/
template
_utlConcept Integral = std::is_integral::value;
/*!
* Signed Integral
*/
template
_utlConcept SignedIntegral = Integral && std::is_signed::value;
/*!
* Unsigned Integral
*/
template
_utlConcept UnsignedIntegral = Integral && !std::is_signed::value;
template
_utlConcept MoveAssignable = std::is_move_assignable::value;
template
_utlConcept CopyAssignable = std::is_copy_assignable::value;
/*!
* Assignable
* \note != std:: on CommonReference
*/
template
_utlConcept Assignable =
#if CXX_CONCEPTS
std::is_lvalue_reference::value &&
// CommonReference<
// const std::remove_reference_t&,
// const std::remove_reference_t&> &&
requires(LHS lhs, RHS&& rhs) {
lhs = std::forward(rhs);
requires Same<
decltype(lhs = std::forward(rhs)), LHS
>;
};
#else
std::is_assignable::value;
#endif
/*!
* Swappable, SwappableWith
*/
//! @{
#if CXX_VER < CXX_VER_STD_17
namespace swappable_with_impl {
struct is_swappable_with_ {
// can apply std::swap
template(), std::declval<_Up&>())),
typename
= decltype(std::swap(std::declval<_Up&>(), std::declval<_Tp&>()))>
static meta::true_ check(int);
// can not apply std::swap
template static meta::false_ check(...);
};
}
template
struct is_swappable_with
: swappable_with_impl::is_swappable_with_ {
using type = decltype(check<_Tp, _Up>(0));
};
#else
template
using is_swappable = std::is_swappable;
template
using is_swappable_with = std::is_swappable_with;
#endif
// != std:: on CommonReference
template
_utlConcept SwappableWith =
is_swappable_with::type::value &&
is_swappable_with::type::value &&
is_swappable_with::type::value &&
is_swappable_with::type::value;
// std::CommonReference<
// const std::remove_reference_t&,
// const std::remove_reference_t&
// >;
// != std:: we use is_swappable_with now is_swappable
template
_utlConcept Swappable = is_swappable_with::type::value;
//! @}
/*!
* Destructible
*/
template
_utlConcept Destructible = std::is_nothrow_destructible::value;
/*!
* Constructible
*/
template
_utlConcept Constructible =
Destructible && std::is_constructible::value;
/*!
* DefaultConstructible
*/
template
_utlConcept DefaultConstructible = Constructible;
/*!
* MoveConstructible
* \note
* Another approach would be std::is_move_constructible::value;
*/
template
_utlConcept MoveConstructible =
Constructible && ConvertibleTo;
/*!
* CopyConstructible
*/
template
_utlConcept CopyConstructible =
MoveConstructible &&
Constructible> && ConvertibleTo<_ref_t, T> &&
Constructible> && ConvertibleTo, T> &&
Constructible && ConvertibleTo;
/*!
* Movable
*/
template
_utlConcept Movable =
std::is_object::value &&
MoveConstructible &&
Assignable<_ref_t, T> &&
Swappable;
/*!
* Copyable
*/
template
_utlConcept Copyable =
CopyConstructible &&
Movable &&
Assignable<_ref_t, const _ref_t>;
/*!
* Boolean
*/
#if CXX_CONCEPTS
template
_utlConcept Boolean =
Movable> &&
requires(const std::remove_reference_t& b1,
const std::remove_reference_t& b2, const bool a) {
requires ConvertibleTo&, bool>;
!b1; requires ConvertibleTo;
b1 && a; requires Same;
b1 || a; requires Same;
b1 && b2; requires Same;
a && b2; requires Same;
b1 || b2; requires Same;
a || b2; requires Same;
b1 == b2; requires ConvertibleTo;
b1 == a; requires ConvertibleTo;
a == b2; requires ConvertibleTo;
b1 != b2; requires ConvertibleTo;
b1 != a; requires ConvertibleTo;
a != b2; requires ConvertibleTo;
};
#else
namespace details {
// template using try_op_not_ = decltype(!std::declval>());
// template using try_op_eq_ = decltype(std::declval>() == std::declval>());
// template using try_op_neq_ = decltype(std::declval>() != std::declval>());
// template using try_op_and_ = decltype(std::declval>() && std::declval>());
// template using try_op_or_ = decltype(std::declval>() || std::declval>());
//
// template
// struct is_boolean__ {
// using type = meta::and_ <
// meta::is_detected,
// meta::is_detected,
// meta::is_detected,
// meta::is_detected,
// meta::is_detected
// >;
// };
template
struct is_boolean_ {
using type = meta::false_;
};
template
struct is_boolean_ >())>,
meta::use_if_same_t