2012-05-25 15:19:07 +10:00
|
|
|
/*
|
2018-08-04 15:14:06 +10:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
2012-05-25 15:19:07 +10:00
|
|
|
*
|
2018-11-01 16:30:56 +11:00
|
|
|
* Copyright 2012-2018 Danny Robson <danny@nerdcruft.net>
|
2012-05-25 15:19:07 +10:00
|
|
|
*/
|
|
|
|
|
2018-11-01 16:30:56 +11:00
|
|
|
#pragma once
|
2012-05-25 15:19:07 +10:00
|
|
|
|
2015-03-10 22:52:17 +11:00
|
|
|
#include <memory>
|
2018-11-01 16:30:56 +11:00
|
|
|
#include <tuple>
|
2014-07-15 19:51:23 +10:00
|
|
|
#include <type_traits>
|
|
|
|
|
2017-09-08 14:10:57 +10:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// applies a series of type-modifiers to a provided type.
|
|
|
|
///
|
|
|
|
/// modifiers are applied in the order they are provided.
|
|
|
|
///
|
|
|
|
/// use without any modifiers results in the identity modifier.
|
2018-08-05 14:42:02 +10:00
|
|
|
namespace cruft::types {
|
2017-09-08 14:10:57 +10:00
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename TypeT, template <typename> class ...ModifierT>
|
2018-06-21 13:19:21 +10:00
|
|
|
struct compose;
|
2017-09-08 14:10:57 +10:00
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename TypeT>
|
2018-06-21 13:19:21 +10:00
|
|
|
struct compose<TypeT> { using type = TypeT; };
|
2017-09-08 14:10:57 +10:00
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <
|
|
|
|
typename TypeT,
|
|
|
|
template <typename> class HeadT,
|
|
|
|
template <typename> class ...TailT
|
|
|
|
>
|
2018-06-21 13:19:21 +10:00
|
|
|
struct compose<TypeT, HeadT, TailT...> : public compose<
|
2017-09-08 14:10:57 +10:00
|
|
|
typename HeadT<TypeT>::type,
|
|
|
|
TailT...
|
|
|
|
> { };
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename T, template <typename> class ...Args>
|
2018-06-21 13:19:21 +10:00
|
|
|
using chain_t = typename compose<T, Args...>::type;
|
2017-09-08 14:10:57 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
namespace cruft::types {
|
2018-06-22 17:41:36 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/// CXX20: An early implementation of std::identity_t and related
|
|
|
|
/// structures; useful for disabling template argument deduction, and
|
|
|
|
/// higher order template functions.
|
|
|
|
template <typename T>
|
|
|
|
struct identity { using type = T; };
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
|
|
using identity_t = typename identity<T>::type;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2012-05-25 15:19:07 +10:00
|
|
|
template <typename T> struct is_dereferencable : std::false_type { };
|
|
|
|
template <typename T> struct is_dereferencable<T*> : std::true_type { };
|
|
|
|
template <typename T> struct is_dereferencable<std::unique_ptr<T>> : std::true_type { };
|
|
|
|
template <typename T> struct is_dereferencable<std::shared_ptr<T>> : std::true_type { };
|
|
|
|
template <typename T> struct is_dereferencable<std::weak_ptr<T>> : std::true_type { };
|
|
|
|
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2012-05-25 15:19:07 +10:00
|
|
|
template <typename T> struct dereferenced_type {
|
|
|
|
typedef typename std::enable_if<
|
|
|
|
std::is_pointer<T>::value,
|
|
|
|
std::remove_pointer<T>
|
|
|
|
>::type type;
|
|
|
|
};
|
|
|
|
|
2014-07-15 19:51:23 +10:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2012-05-25 15:19:07 +10:00
|
|
|
template <typename T> struct dereferenced_type<std::unique_ptr<T>> { typedef T type; };
|
|
|
|
template <typename T> struct dereferenced_type<std::shared_ptr<T>> { typedef T type; };
|
|
|
|
template <typename T> struct dereferenced_type<std::weak_ptr<T>> { typedef T type; };
|
|
|
|
|
2012-05-25 15:30:11 +10:00
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-02-18 02:33:51 +11:00
|
|
|
/// find the unsigned version of a type if one exists
|
2015-02-17 16:20:15 +11:00
|
|
|
template <typename T>
|
|
|
|
struct try_unsigned
|
|
|
|
{
|
2012-05-25 15:30:11 +10:00
|
|
|
typedef typename std::make_unsigned<T>::type type;
|
|
|
|
};
|
2015-02-17 16:20:15 +11:00
|
|
|
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2015-02-17 16:20:15 +11:00
|
|
|
template <> struct try_unsigned<double> { typedef double type; };
|
|
|
|
template <> struct try_unsigned<float > { typedef float type; };
|
2012-05-25 15:30:11 +10:00
|
|
|
|
2014-07-15 19:51:42 +10:00
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-03-20 01:32:28 +11:00
|
|
|
/// find the signed version of a type if one exists
|
|
|
|
template <typename T>
|
|
|
|
struct try_signed
|
|
|
|
{
|
|
|
|
typedef typename std::make_signed<T>::type type;
|
|
|
|
};
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2015-03-20 01:32:28 +11:00
|
|
|
template <> struct try_signed<double> { typedef double type; };
|
|
|
|
template <> struct try_signed<float > { typedef float type; };
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-02-18 02:33:51 +11:00
|
|
|
/// checks if a type can be converted in all cases without modification
|
2014-07-15 19:51:42 +10:00
|
|
|
template <typename T, typename U> struct is_lossless_cast : std::enable_if<
|
|
|
|
std::is_integral<T>::value &&
|
|
|
|
std::is_integral<U>::value &&
|
|
|
|
std::is_signed<T>::value == std::is_signed<U>::value &&
|
|
|
|
sizeof (T) <= sizeof (U),
|
|
|
|
|
|
|
|
std::true_type
|
|
|
|
>::value { };
|
|
|
|
|
2015-03-10 22:52:17 +11:00
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2017-09-06 13:31:02 +10:00
|
|
|
template <typename T> struct remove_restrict { using type = T; };
|
|
|
|
template <typename T> struct remove_restrict<T *restrict> { using type = T*; };
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
using remove_restrict_t = typename remove_restrict<T>::type;
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// removes the noexcept type specifier from invokable types
|
2017-09-08 14:19:03 +10:00
|
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
|
|
struct remove_noexcept
|
|
|
|
{ using type = T; };
|
2015-03-10 22:52:17 +11:00
|
|
|
|
|
|
|
|
2017-09-08 14:19:03 +10:00
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ResultT, typename ...Args>
|
|
|
|
struct remove_noexcept<ResultT(&)(Args...) noexcept> {
|
|
|
|
using type = ResultT(&)(Args...);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ResultT, typename ...Args>
|
|
|
|
struct remove_noexcept<ResultT(*const)(Args...) noexcept> {
|
|
|
|
using type = ResultT(*const)(Args...);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ResultT, typename ...Args>
|
|
|
|
struct remove_noexcept<ResultT(*)(Args...) noexcept> {
|
|
|
|
using type = ResultT(*)(Args...);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ClassT, typename ResultT, typename ...Args>
|
|
|
|
struct remove_noexcept<ResultT(ClassT::*)(Args...) noexcept> {
|
|
|
|
using type = ResultT(ClassT::*)(Args...);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ClassT, typename ResultT, typename ...Args>
|
|
|
|
struct remove_noexcept<ResultT(ClassT::*)(Args...) const noexcept> {
|
|
|
|
using type = ResultT(ClassT::*)(Args...) const;
|
|
|
|
};
|
2015-03-10 22:52:17 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2017-09-08 14:19:03 +10:00
|
|
|
template <typename T>
|
|
|
|
struct remove_noexcept : public detail::remove_noexcept<T> { };
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
|
|
using remove_noexcept_t = typename remove_noexcept<T>::type;
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// removes any `const' qualifier from the supplied member function
|
|
|
|
template <typename FuncT>
|
|
|
|
struct remove_member_const {
|
|
|
|
using type = FuncT;
|
2015-03-10 22:52:17 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-09-06 13:31:02 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
2017-09-08 14:19:28 +10:00
|
|
|
template <typename ClassT, typename ReturnT, typename ...Args>
|
|
|
|
struct remove_member_const<ReturnT(ClassT::*const)(Args...) const> {
|
|
|
|
using type = ReturnT(ClassT::*const)(Args...);
|
2017-09-06 13:31:02 +10:00
|
|
|
};
|
|
|
|
|
2016-03-16 19:27:39 +11:00
|
|
|
|
2017-09-06 13:31:02 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
2017-09-08 14:19:28 +10:00
|
|
|
template <typename ClassT, typename ReturnT, typename ...Args>
|
|
|
|
struct remove_member_const<ReturnT(ClassT::*)(Args...) const> {
|
|
|
|
using type = ReturnT(ClassT::*)(Args...);
|
|
|
|
};
|
2017-09-06 13:31:02 +10:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// type traits class for querying invokable type return values and argument
|
|
|
|
/// types.
|
|
|
|
///
|
|
|
|
/// if the type is invokable the alias `return_type' will be defined for the
|
|
|
|
/// return type, and the alias tuple `argument_types' will be defined for the
|
|
|
|
/// arguments;
|
2017-09-08 14:19:46 +10:00
|
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
|
|
struct func_traits { };
|
2017-09-06 13:31:02 +10:00
|
|
|
|
|
|
|
|
2017-09-08 14:19:46 +10:00
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ClassT, typename ResultT, typename ...Args>
|
|
|
|
struct func_traits<ResultT(ClassT::*)(Args...)> {
|
|
|
|
using return_type = ResultT;
|
|
|
|
using argument_types = std::tuple<Args...>;
|
|
|
|
};
|
2017-09-06 13:31:02 +10:00
|
|
|
|
|
|
|
|
2017-09-08 14:19:46 +10:00
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ResultT, typename ...Args>
|
|
|
|
struct func_traits<ResultT(*)(Args...)> {
|
|
|
|
using return_type = ResultT;
|
|
|
|
using argument_types = std::tuple<Args...>;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ResultT, typename ...Args>
|
|
|
|
struct func_traits<ResultT(&)(Args...)> {
|
|
|
|
using return_type = ResultT;
|
|
|
|
using argument_types = std::tuple<Args...>;
|
|
|
|
};
|
2017-09-06 13:31:02 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2017-09-08 14:19:46 +10:00
|
|
|
template <typename T>
|
|
|
|
struct func_traits : public ::detail::func_traits<
|
|
|
|
// we apply as many transforms as possible before palming it off to the
|
|
|
|
// detail class so that we don't have to write as many distinct cases.
|
2018-08-05 14:42:02 +10:00
|
|
|
::cruft::types::chain_t<T,
|
2017-09-08 14:19:46 +10:00
|
|
|
std::remove_cv,
|
|
|
|
std::decay,
|
|
|
|
remove_member_const,
|
|
|
|
remove_noexcept
|
|
|
|
>
|
|
|
|
> {
|
|
|
|
// we may as well record the underlying type here. it might prove useful
|
|
|
|
// to someone.
|
|
|
|
using type = T;
|
2017-09-06 13:31:02 +10:00
|
|
|
};
|
2016-03-16 19:27:39 +11:00
|
|
|
|
|
|
|
|
2018-03-03 13:41:35 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename FunctionT>
|
|
|
|
struct function_argument_count : public std::integral_constant<
|
|
|
|
size_t,
|
|
|
|
std::tuple_size_v<
|
|
|
|
typename func_traits<FunctionT>::argument_types
|
|
|
|
>
|
|
|
|
> { };
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <typename FunctionT>
|
|
|
|
constexpr auto function_argument_count_v = function_argument_count<FunctionT>::value;
|
|
|
|
|
|
|
|
|
2017-09-10 12:42:18 +10:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <std::size_t N, typename FuncT>
|
|
|
|
struct nth_argument : std::tuple_element<
|
|
|
|
N,
|
|
|
|
typename func_traits<FuncT>::argument_types
|
|
|
|
> { };
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t N, typename FuncT>
|
|
|
|
using nth_argument_t = typename nth_argument<N, FuncT>::type;
|
|
|
|
|
|
|
|
|
2018-01-13 13:48:58 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename T, typename = std::void_t<>>
|
|
|
|
struct is_container : public std::false_type {};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct is_container<
|
|
|
|
T,
|
|
|
|
std::void_t<
|
|
|
|
typename T::value_type,
|
|
|
|
typename T::reference,
|
|
|
|
typename T::const_reference,
|
|
|
|
typename T::iterator,
|
|
|
|
typename T::const_iterator,
|
|
|
|
typename T::difference_type,
|
|
|
|
typename T::size_type
|
|
|
|
>
|
|
|
|
> : public std::true_type {};
|
|
|
|
|
|
|
|
|
2018-04-18 21:47:10 +10:00
|
|
|
template <size_t N, typename ValueT>
|
|
|
|
struct is_container<std::array<ValueT,N>> : public std::true_type {};
|
|
|
|
|
|
|
|
|
2018-01-13 13:48:58 +11:00
|
|
|
template <typename T>
|
|
|
|
constexpr auto is_container_v = is_container<T>::value;
|
|
|
|
|
|
|
|
|
2017-12-28 17:48:21 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <string>
|
|
|
|
#include <array>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct is_contiguous : public std::false_type {};
|
|
|
|
|
|
|
|
template <typename CharT, typename Traits, typename Allocator>
|
|
|
|
struct is_contiguous<std::basic_string<CharT, Traits, Allocator>> : public std::true_type {};
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
struct is_contiguous<std::array<T,N>>: public std::true_type {};
|
|
|
|
|
|
|
|
template <typename T, typename Allocator>
|
|
|
|
struct is_contiguous<std::vector<T,Allocator>>: public std::true_type {};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
constexpr auto is_contiguous_v = is_contiguous<T>::value;
|
|
|
|
|
2018-03-12 23:11:26 +11:00
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
namespace cruft {
|
2018-08-01 17:18:59 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// stores the type of '::value_type' for a given type, or the type itself if
|
|
|
|
/// it does not have such a type.
|
|
|
|
template <typename ValueT, typename = std::void_t<>>
|
|
|
|
struct inner_type {
|
|
|
|
using type = ValueT;
|
|
|
|
};
|
2018-03-12 23:11:26 +11:00
|
|
|
|
|
|
|
|
2018-08-01 17:18:59 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <typename ValueT>
|
|
|
|
struct inner_type<
|
|
|
|
ValueT,
|
|
|
|
std::void_t<typename ValueT::value_type>
|
|
|
|
> {
|
|
|
|
using type = typename ValueT::value_type;
|
|
|
|
};
|
2018-03-12 23:11:26 +11:00
|
|
|
|
|
|
|
|
2018-08-01 17:18:59 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <typename ValueT>
|
|
|
|
using inner_type_t = typename inner_type<ValueT>::type;
|
|
|
|
}
|
2018-03-12 23:11:26 +11:00
|
|
|
|
2018-03-15 15:21:53 +11:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <
|
|
|
|
template <typename...> class TemplateT,
|
|
|
|
typename QueryT
|
|
|
|
>
|
|
|
|
struct is_same_template_template : public std::false_type {};
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <
|
|
|
|
template <typename...> class TemplateT,
|
|
|
|
template <typename...> class QueryT,
|
|
|
|
typename ...Args
|
|
|
|
> struct is_same_template_template<
|
|
|
|
TemplateT,
|
|
|
|
QueryT<Args...>
|
|
|
|
> : public std::conditional_t<
|
|
|
|
std::is_same_v<
|
|
|
|
TemplateT<Args...>,
|
|
|
|
QueryT<Args...>
|
|
|
|
>,
|
|
|
|
std::true_type,
|
|
|
|
std::false_type
|
|
|
|
> { };
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <template <typename...> class TemplateT, typename QueryT>
|
|
|
|
constexpr auto is_same_template_template_v = is_same_template_template<TemplateT,QueryT>::value;
|
|
|
|
|
2018-08-01 18:39:01 +10:00
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
namespace cruft::traits {
|
2018-08-01 18:39:01 +10:00
|
|
|
/// combines an abitrary sequence of type traits into one result
|
|
|
|
template <
|
|
|
|
typename ValueT,
|
|
|
|
template <typename> class ...Traits
|
|
|
|
> struct all : public std::integral_constant<
|
|
|
|
bool,
|
|
|
|
(Traits<ValueT>::value && ...)
|
|
|
|
> { };
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2018-08-05 14:42:02 +10:00
|
|
|
namespace cruft {
|
2018-08-01 18:39:01 +10:00
|
|
|
/// tests if the type is a signed integer type
|
|
|
|
template <typename ValueT>
|
|
|
|
struct is_signed_integral : public traits::all<
|
|
|
|
ValueT, std::is_signed, std::is_integral
|
|
|
|
> { };
|
|
|
|
|
|
|
|
|
|
|
|
template <typename ValueT>
|
|
|
|
constexpr auto is_signed_integral_v = is_signed_integral<ValueT>::value;
|
|
|
|
|
|
|
|
|
|
|
|
/// tests if the type is an unsigned integer type
|
|
|
|
template <typename ValueT>
|
|
|
|
struct is_unsigned_integral : public traits::all<
|
|
|
|
ValueT, std::is_unsigned, std::is_integral
|
|
|
|
> { };
|
|
|
|
|
|
|
|
template <typename ValueT>
|
|
|
|
constexpr auto is_unsigned_integral_v = is_unsigned_integral<ValueT>::value;
|
|
|
|
|
|
|
|
|
|
|
|
/// tests if the type has the same basic fundamental type.
|
|
|
|
///
|
|
|
|
/// ie, both float, signed, unsigned, or char.
|
|
|
|
template <typename ValueA, typename ValueB>
|
|
|
|
struct is_same_basic_type {
|
|
|
|
static constexpr bool value =
|
|
|
|
(std::is_floating_point_v<ValueA> && std::is_floating_point_v<ValueB>) ||
|
|
|
|
(is_unsigned_integral_v<ValueA> && is_unsigned_integral_v<ValueB>) ||
|
|
|
|
(is_signed_integral_v<ValueA> && is_signed_integral_v<ValueB>) ||
|
|
|
|
(std::is_same_v<char,ValueA> && std::is_same_v<char,ValueB>);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename ValueA, typename ValueB>
|
|
|
|
constexpr auto is_same_basic_type_v = is_same_basic_type<ValueA,ValueB>::value;
|
|
|
|
|
2018-11-01 16:30:56 +11:00
|
|
|
|
|
|
|
/// Tests if an object is 'tuple-like'; ie, it responds to std::get,
|
|
|
|
/// std::tuple_size, and std::tuple_element.
|
|
|
|
template <typename, typename = void>
|
|
|
|
struct is_tuple_like : public std::false_type {};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct is_tuple_like<
|
|
|
|
T,
|
|
|
|
std::void_t<
|
|
|
|
decltype (std::get<0> (std::declval<T> ())),
|
|
|
|
decltype (std::tuple_size_v<T>),
|
|
|
|
std::tuple_element_t<0,T>
|
|
|
|
>
|
|
|
|
> : public std::true_type {};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
constexpr auto is_tuple_like_v = is_tuple_like<T>::value;
|
2018-11-19 15:35:05 +11:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/// Tests if a type is orderable; ie, if it supports the less than operator
|
|
|
|
///
|
|
|
|
/// We use void_t to detect the presence of an appropriate operator<
|
|
|
|
template <
|
|
|
|
typename ValueT,
|
|
|
|
typename = std::void_t<>
|
|
|
|
>
|
|
|
|
struct is_orderable : public std::false_type {};
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ValueT>
|
|
|
|
struct is_orderable<
|
|
|
|
ValueT,
|
|
|
|
std::void_t<
|
|
|
|
decltype (
|
|
|
|
std::declval<ValueT> () < std::declval<ValueT> ()
|
|
|
|
)
|
|
|
|
>
|
|
|
|
> : std::true_type {};
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
template <typename ValueT>
|
|
|
|
constexpr auto is_orderable_v = is_orderable<ValueT>::value;
|
|
|
|
};
|