Permalink
/** @addtogroup cometa | |
* @{ | |
*/ | |
#pragma once | |
#include "cident.h" | |
#include <algorithm> | |
#include <array> | |
#include <tuple> | |
#include <type_traits> | |
#include <vector> | |
#pragma clang diagnostic push | |
#pragma clang diagnostic ignored "-Wshadow" | |
namespace cometa | |
{ | |
using std::size_t; | |
using std::ptrdiff_t; | |
using pvoid = void*; | |
template <typename...> | |
using void_t = void; | |
// Workaround for GCC 4.8 | |
template <typename T> | |
constexpr const T& const_max(const T& x, const T& y) | |
{ | |
return x > y ? x : y; | |
} | |
template <typename T> | |
constexpr const T& const_min(const T& x, const T& y) | |
{ | |
return x < y ? x : y; | |
} | |
namespace details | |
{ | |
constexpr inline bool args_or() { return false; } | |
template <typename... Ts> | |
constexpr inline bool args_or(bool x, Ts... rest) | |
{ | |
return x || args_or(rest...); | |
} | |
constexpr inline bool args_and() { return true; } | |
template <typename... Ts> | |
constexpr inline bool args_and(bool x, Ts... rest) | |
{ | |
return x && args_and(rest...); | |
} | |
template <typename T, typename Enable = void> | |
struct is_pod_impl : std::false_type | |
{ | |
}; | |
template <typename T> | |
struct is_pod_impl<T, void_t<decltype(T::is_pod)>> : std::integral_constant<bool, T::is_pod> | |
{ | |
}; | |
} | |
template <typename... Ts> | |
struct or_t : std::integral_constant<bool, details::args_or(Ts::value...)> | |
{ | |
}; | |
template <typename... Ts> | |
struct and_t : std::integral_constant<bool, details::args_and(Ts::value...)> | |
{ | |
}; | |
template <typename T> | |
struct not_t : std::integral_constant<bool, !T::value> | |
{ | |
}; | |
constexpr size_t max_size_t = size_t(-1); | |
template <typename... T> | |
using common_type = typename std::common_type<T...>::type; | |
template <typename T> | |
using result_of = typename std::result_of<T>::type; | |
template <bool Condition, typename Type = void> | |
using enable_if = typename std::enable_if<Condition, Type>::type; | |
template <bool Condition, typename T, typename F> | |
using conditional = typename std::conditional<Condition, T, F>::type; | |
template <typename T> | |
using remove_reference = typename std::remove_reference<T>::type; | |
template <typename T> | |
using remove_cv = typename std::remove_cv<T>::type; | |
template <typename T> | |
using remove_pointer = typename std::remove_pointer<T>::type; | |
template <typename T> | |
using remove_extent = typename std::remove_extent<T>::type; | |
template <typename T> | |
using remove_const = typename std::remove_const<T>::type; | |
template <typename T> | |
using underlying_type = typename std::underlying_type<T>::type; | |
template <typename T> | |
using is_pod = or_t<std::is_pod<T>, details::is_pod_impl<T>>; | |
template <typename T> | |
using is_class = std::is_class<T>; | |
template <typename T> | |
using is_const = std::is_const<T>; | |
template <typename T> | |
using is_pointer = std::is_pointer<T>; | |
template <typename T> | |
using is_array = std::is_array<T>; | |
template <typename T> | |
using is_void = std::is_void<T>; | |
template <typename T1, typename T2> | |
using is_same = std::is_same<T1, T2>; | |
template <typename T> | |
using is_template_arg = std::integral_constant<bool, std::is_integral<T>::value || std::is_enum<T>::value>; | |
template <typename T> | |
using decay = typename std::decay<T>::type; | |
template <typename... T> | |
using decay_common = decay<common_type<T...>>; | |
template <typename T1, typename T2 = void, typename... Ts> | |
constexpr size_t typeindex() | |
{ | |
return is_same<T1, T2>() ? 0 : 1 + typeindex<T1, Ts...>(); | |
} | |
template <typename T> | |
struct compound_type_traits | |
{ | |
constexpr static size_t width = 1; | |
constexpr static size_t deep_width = width; | |
using subtype = T; | |
using deep_subtype = T; | |
constexpr static size_t depth = 0; | |
constexpr static bool is_scalar = true; | |
template <typename U> | |
using rebind = U; | |
template <typename U> | |
using deep_rebind = U; | |
CMT_INLINE static constexpr const subtype& at(const T& value, size_t /*index*/) { return value; } | |
}; | |
template <typename T> | |
using is_compound = std::integral_constant<bool, !compound_type_traits<decay<T>>::is_scalar>; | |
template <typename T> | |
using subtype = typename compound_type_traits<T>::subtype; | |
template <typename T> | |
using deep_subtype = typename compound_type_traits<T>::deep_subtype; | |
template <typename T, typename SubType> | |
using rebind_subtype = typename compound_type_traits<T>::template rebind<SubType>; | |
template <typename T, typename SubType> | |
using deep_rebind = typename compound_type_traits<T>::template deep_rebind<SubType>; | |
template <typename T> | |
struct compound_type_traits<std::pair<T, T>> | |
{ | |
constexpr static size_t width = 2; | |
constexpr static size_t deep_width = width * compound_type_traits<T>::width; | |
using subtype = T; | |
using deep_subtype = cometa::deep_subtype<T>; | |
constexpr static bool is_scalar = false; | |
constexpr static size_t depth = cometa::compound_type_traits<T>::depth + 1; | |
template <typename U> | |
using rebind = std::pair<U, U>; | |
template <typename U> | |
using deep_rebind = std::pair<cometa::deep_rebind<subtype, U>, cometa::deep_rebind<subtype, U>>; | |
CMT_INLINE static constexpr const subtype& at(const std::pair<subtype, subtype>& value, size_t index) | |
{ | |
return index == 0 ? value.first : value.second; | |
} | |
}; | |
namespace ops | |
{ | |
struct empty | |
{ | |
}; | |
} | |
template <typename T, T val> | |
struct cval_t : ops::empty | |
{ | |
constexpr static T value = val; | |
constexpr cval_t() noexcept = default; | |
constexpr cval_t(const cval_t&) noexcept = default; | |
constexpr cval_t(cval_t&&) noexcept = default; | |
typedef T value_type; | |
typedef cval_t type; | |
constexpr operator value_type() const { return value; } | |
constexpr value_type operator()() const { return value; } | |
}; | |
template <typename T, T value> | |
constexpr inline T val_of(cval_t<T, value>) | |
{ | |
return value; | |
} | |
template <typename T> | |
constexpr inline T val_of(T value) | |
{ | |
return value; | |
} | |
template <typename T> | |
constexpr inline bool is_constant_val(T) | |
{ | |
return false; | |
} | |
template <typename T, T value> | |
constexpr inline bool is_constant_val(cval_t<T, value>) | |
{ | |
return true; | |
} | |
namespace details | |
{ | |
template <typename T> | |
struct inherit : T | |
{ | |
}; | |
template <typename T, typename Enable = void> | |
struct is_inheritable_impl : std::false_type | |
{ | |
}; | |
template <typename T> | |
struct is_inheritable_impl<T, void_t<inherit<T>>> : std::true_type | |
{ | |
}; | |
template <typename T> | |
struct is_val_impl : std::false_type | |
{ | |
}; | |
template <typename T, T val> | |
struct is_val_impl<cval_t<T, val>> : std::true_type | |
{ | |
}; | |
} | |
template <typename T> | |
using is_inheritable = typename details::is_inheritable_impl<T>::type; | |
template <typename T> | |
using is_val_t = typename details::is_val_impl<T>::type; | |
template <bool val> | |
using cbool_t = cval_t<bool, val>; | |
template <int val> | |
using cint_t = cval_t<int, val>; | |
template <unsigned val> | |
using cuint_t = cval_t<unsigned, val>; | |
template <size_t val> | |
using csize_t = cval_t<size_t, val>; | |
template <typename T, T val> | |
constexpr cval_t<T, val> cval{}; | |
template <bool val> | |
constexpr cbool_t<val> cbool{}; | |
using cfalse_t = cbool_t<false>; | |
using ctrue_t = cbool_t<true>; | |
constexpr ctrue_t ctrue{}; | |
constexpr cfalse_t cfalse{}; | |
template <int val> | |
constexpr cint_t<val> cint{}; | |
template <unsigned val> | |
constexpr cuint_t<val> cuint{}; | |
template <size_t val> | |
constexpr csize_t<val> csize{}; | |
namespace details | |
{ | |
template <size_t index, typename T, T first, T... rest> | |
struct get_nth : get_nth<index - 1, T, rest...> | |
{ | |
}; | |
template <typename T, T first, T... rest> | |
struct get_nth<0, T, first, rest...> | |
{ | |
constexpr static T value = first; | |
}; | |
template <size_t index, typename... Types> | |
struct get_nth_type; | |
template <size_t index, typename first, typename... rest> | |
struct get_nth_type<index, first, rest...> : get_nth_type<index - 1, rest...> | |
{ | |
}; | |
template <typename first, typename... rest> | |
struct get_nth_type<0, first, rest...> | |
{ | |
using type = first; | |
}; | |
template <size_t index> | |
struct get_nth_type<index> | |
{ | |
}; | |
} | |
template <typename T, T... values> | |
struct cvals_t : ops::empty | |
{ | |
using type = cvals_t<T, values...>; | |
constexpr static size_t size() { return sizeof...(values); } | |
template <size_t index> | |
constexpr T operator[](csize_t<index>) | |
{ | |
return get(csize<index>); | |
} | |
template <size_t index> | |
constexpr static T get(csize_t<index> = csize_t<index>()) | |
{ | |
return details::get_nth<index, T, values...>::value; | |
} | |
constexpr static T front() { return get(csize<0>); } | |
constexpr static T back() { return get(csize<size() - 1>); } | |
static const T* begin() { return array(); } | |
static const T* end() { return array() + size(); } | |
static const T* array() | |
{ | |
static const T arr[] = { values... }; | |
return &arr[0]; | |
} | |
template <size_t... indices> | |
constexpr cvals_t<T, details::get_nth<indices, T, values...>::value...> operator[]( | |
cvals_t<size_t, indices...>) const | |
{ | |
return {}; | |
} | |
}; | |
template <typename T> | |
struct cvals_t<T> : ops::empty | |
{ | |
using type = cvals_t<T>; | |
constexpr static size_t size() { return 0; } | |
}; | |
template <bool... values> | |
using cbools_t = cvals_t<bool, values...>; | |
template <int... values> | |
using cints_t = cvals_t<int, values...>; | |
template <char... values> | |
using cchars_t = cvals_t<char, values...>; | |
template <unsigned... values> | |
using cuints_t = cvals_t<unsigned, values...>; | |
template <size_t... values> | |
using csizes_t = cvals_t<size_t, values...>; | |
template <size_t... values> | |
using elements_t = cvals_t<size_t, values...>; | |
template <typename T, T... values> | |
constexpr cvals_t<T, values...> cvals{}; | |
template <bool... vals> | |
constexpr cbools_t<vals...> cbools{}; | |
constexpr cbools_t<false, true> cfalse_true{}; | |
template <int... vals> | |
constexpr cints_t<vals...> cints{}; | |
template <char... vals> | |
constexpr cchars_t<vals...> cchars{}; | |
template <unsigned... vals> | |
constexpr cuints_t<vals...> cuints{}; | |
template <size_t... vals> | |
constexpr csizes_t<vals...> csizes{}; | |
template <size_t... vals> | |
constexpr elements_t<vals...> elements{}; | |
template <typename T> | |
constexpr inline T csum(cvals_t<T>) | |
{ | |
return 0; | |
} | |
template <typename T, T first, T... rest> | |
constexpr inline T csum(cvals_t<T, first, rest...>) | |
{ | |
return first + csum(cvals<T, rest...>); | |
} | |
template <typename T> | |
constexpr inline T cprod(cvals_t<T>) | |
{ | |
return 1; | |
} | |
template <typename T, T first, T... rest> | |
constexpr inline T cprod(cvals_t<T, first, rest...>) | |
{ | |
return first * cprod(cvals<T, rest...>); | |
} | |
template <typename T> | |
struct ctype_t | |
{ | |
using type = T; | |
}; | |
template <typename T> | |
using type_of = typename T::type; | |
template <typename T> | |
constexpr ctype_t<T> ctype{}; | |
template <typename... Types> | |
struct ctypes_t | |
{ | |
constexpr static size_t size() { return sizeof...(Types); } | |
template <size_t index> | |
using nth = typename details::get_nth_type<index, Types...>::type; | |
template <size_t index> | |
constexpr static auto get(csize_t<index>) -> ctype_t<nth<index>> | |
{ | |
return {}; | |
} | |
}; | |
template <typename... Ts> | |
constexpr ctypes_t<Ts...> ctypes{}; | |
namespace details | |
{ | |
template <typename T1, typename T2> | |
struct concat_impl; | |
template <typename T, T... values1, T... values2> | |
struct concat_impl<cvals_t<T, values1...>, cvals_t<T, values2...>> | |
{ | |
using type = cvals_t<T, values1..., values2...>; | |
}; | |
template <typename... types1, typename... types2> | |
struct concat_impl<ctypes_t<types1...>, ctypes_t<types2...>> | |
{ | |
using type = ctypes_t<types1..., types2...>; | |
}; | |
} | |
template <typename T1, typename T2> | |
using concat_lists = typename details::concat_impl<T1, T2>::type; | |
template <typename T1, typename T2> | |
constexpr inline concat_lists<T1, T2> cconcat(T1, T2) | |
{ | |
return {}; | |
} | |
namespace details | |
{ | |
template <typename> | |
struct function_arguments_impl; | |
template <typename Ret, typename... Args> | |
struct function_arguments_impl<Ret (*)(Args...)> | |
{ | |
using result = Ret; | |
using args = ctypes_t<Args...>; | |
}; | |
template <typename Class, typename Ret, typename... Args> | |
struct function_arguments_impl<Ret (Class::*)(Args...)> | |
{ | |
using result = Ret; | |
using args = ctypes_t<Args...>; | |
}; | |
template <typename Class, typename Ret, typename... Args> | |
struct function_arguments_impl<Ret (Class::*)(Args...) const> | |
{ | |
using result = Ret; | |
using args = ctypes_t<Args...>; | |
}; | |
template <typename T1, typename T2> | |
struct filter_impl; | |
template <typename T> | |
struct filter_impl<cvals_t<T>, cvals_t<bool>> | |
{ | |
using type = cvals_t<T>; | |
}; | |
template <typename T, T value, T... values, bool flag, bool... flags> | |
struct filter_impl<cvals_t<T, value, values...>, cvals_t<bool, flag, flags...>> | |
{ | |
using filtered = typename filter_impl<cvals_t<T, values...>, cvals_t<bool, flags...>>::type; | |
using type = conditional<flag, concat_lists<cvals_t<T, value>, filtered>, filtered>; | |
}; | |
} | |
template <typename Fn> | |
using function_arguments = typename details::function_arguments_impl<decltype(&Fn::operator())>::args; | |
template <typename Fn> | |
using function_result = typename details::function_arguments_impl<decltype(&Fn::operator())>::result; | |
template <typename T1, typename T2> | |
using cfilter_t = typename details::filter_impl<T1, T2>::type; | |
template <typename T, T... vals, bool... flags, | |
typename Ret = cfilter_t<cvals_t<T, vals...>, cvals_t<bool, flags...>>> | |
constexpr inline Ret cfilter(cvals_t<T, vals...>, cvals_t<bool, flags...>) | |
{ | |
return Ret{}; | |
} | |
#define CMT_UN_OP(op) \ | |
template <typename T1, T1... vals1, \ | |
typename Ret = cvals_t<decltype(op std::declval<T1>()), (op vals1)...>> \ | |
constexpr inline Ret operator op(cvals_t<T1, vals1...>) \ | |
{ \ | |
return Ret{}; \ | |
} \ | |
template <typename T1, T1 val1, typename Ret = cval_t<decltype(op std::declval<T1>()), (op val1)>> \ | |
constexpr inline Ret operator op(cval_t<T1, val1>) \ | |
{ \ | |
return Ret{}; \ | |
} | |
#define CMT_BIN_OP(op) \ | |
template <typename T1, T1... vals1, typename T2, T2... vals2, \ | |
typename Ret = \ | |
cvals_t<decltype(std::declval<T1>() op std::declval<T2>()), (vals1 op vals2)...>> \ | |
constexpr inline Ret operator op(cvals_t<T1, vals1...>, cvals_t<T2, vals2...>) \ | |
{ \ | |
return Ret{}; \ | |
} \ | |
template <typename T1, T1... vals1, typename T2, T2 val2, \ | |
typename Ret = \ | |
cvals_t<decltype(std::declval<T1>() op std::declval<T2>()), (vals1 op val2)...>> \ | |
constexpr inline Ret operator op(cvals_t<T1, vals1...>, cval_t<T2, val2>) \ | |
{ \ | |
return Ret{}; \ | |
} \ | |
template <typename T1, T1 val1, typename T2, T2... vals2, \ | |
typename Ret = \ | |
cvals_t<decltype(std::declval<T1>() op std::declval<T2>()), (val1 op vals2)...>> \ | |
constexpr inline Ret operator op(cval_t<T1, val1>, cvals_t<T2, vals2...>) \ | |
{ \ | |
return Ret{}; \ | |
} | |
namespace ops | |
{ | |
// clang-format off | |
CMT_UN_OP(-) | |
CMT_UN_OP(+) | |
CMT_UN_OP(~) | |
CMT_UN_OP(!) | |
CMT_BIN_OP(&&) | |
CMT_BIN_OP(||) | |
CMT_BIN_OP(==) | |
CMT_BIN_OP(!=) | |
CMT_BIN_OP(<) | |
CMT_BIN_OP(>) | |
CMT_BIN_OP(<=) | |
CMT_BIN_OP(>=) | |
CMT_BIN_OP(+) | |
CMT_BIN_OP(-) | |
CMT_BIN_OP(*) | |
CMT_BIN_OP(/) | |
CMT_BIN_OP(%) | |
CMT_BIN_OP(<<) | |
CMT_BIN_OP(>>) | |
CMT_BIN_OP(&) | |
CMT_BIN_OP(|) | |
CMT_BIN_OP(^) | |
// clang-format on | |
} | |
namespace details | |
{ | |
template <typename T, size_t Nsize, T Nstart, ptrdiff_t Nstep> | |
struct cvalseq_impl; | |
template <typename T, size_t Nsize, T Nstart, ptrdiff_t Nstep> | |
using cgen_seq = typename cvalseq_impl<T, Nsize, Nstart, Nstep>::type; | |
template <typename T, size_t Nsize, T Nstart, ptrdiff_t Nstep> | |
struct cvalseq_impl : concat_impl<cgen_seq<T, Nsize / 2, Nstart, Nstep>, | |
cgen_seq<T, Nsize - Nsize / 2, Nstart + (Nsize / 2) * Nstep, Nstep>> | |
{ | |
}; | |
template <typename T, T Nstart, ptrdiff_t Nstep> | |
struct cvalseq_impl<T, 0, Nstart, Nstep> : cvals_t<T> | |
{ | |
}; | |
template <typename T, T Nstart, ptrdiff_t Nstep> | |
struct cvalseq_impl<T, 1, Nstart, Nstep> : cvals_t<T, static_cast<T>(Nstart)> | |
{ | |
}; | |
} | |
template <typename T, size_t size, T start = T(), ptrdiff_t step = 1> | |
using cvalseq_t = typename details::cvalseq_impl<T, size, start, step>::type; | |
template <typename T, T begin, T end> | |
constexpr cvalseq_t<T, end - begin, begin> cvalrange{}; | |
template <size_t begin, size_t end> | |
constexpr cvalseq_t<size_t, end - begin, begin> csizerange{}; | |
template <int begin, int end> | |
constexpr cvalseq_t<int, end - begin, begin> cintrange{}; | |
template <unsigned begin, unsigned end> | |
constexpr cvalseq_t<unsigned, end - begin, begin> cuintrange{}; | |
template <typename T, size_t size, T start = T(), ptrdiff_t step = 1> | |
constexpr cvalseq_t<T, size, start, step> cvalseq{}; | |
template <size_t size, size_t start = 0, ptrdiff_t step = 1> | |
constexpr cvalseq_t<size_t, size, start, step> csizeseq{}; | |
template <size_t size, int start = 0, ptrdiff_t step = 1> | |
constexpr cvalseq_t<int, size, start, step> cintseq{}; | |
template <size_t size, unsigned start = 0, ptrdiff_t step = 1> | |
constexpr cvalseq_t<unsigned, size, start, step> cuintseq{}; | |
template <typename... List> | |
using indicesfor_t = cvalseq_t<size_t, sizeof...(List), 0>; | |
template <typename... List> | |
constexpr indicesfor_t<List...> indicesfor{}; | |
namespace details | |
{ | |
template <typename Ret, typename T, typename enable = void_t<>> | |
struct is_returning_type_impl : std::false_type | |
{ | |
}; | |
template <typename Ret, typename Fn, typename... Args> | |
struct is_returning_type_impl<Ret, Fn(Args...), void_t<result_of<Fn(Args...)>>> | |
: std::is_same<Ret, result_of<Fn(Args...)>> | |
{ | |
}; | |
template <typename Fn, typename Args, typename enable = void_t<>> | |
struct is_callable_impl : std::false_type | |
{ | |
}; | |
template <typename Fn, typename... Args> | |
struct is_callable_impl<Fn, ctypes_t<Args...>, void_t<result_of<Fn(Args...)>>> : std::true_type | |
{ | |
}; | |
template <typename T, typename enable = void_t<>> | |
struct is_enabled_impl : std::true_type | |
{ | |
}; | |
template <typename Fn> | |
struct is_enabled_impl<Fn, void_t<decltype(Fn::disabled)>> : std::integral_constant<bool, !Fn::disabled> | |
{ | |
}; | |
template <size_t N> | |
struct unique_enum_impl | |
{ | |
enum class type : size_t | |
{ | |
value = N | |
}; | |
}; | |
template <size_t N> | |
using unique_enum = typename unique_enum_impl<N>::type; | |
#define CMT_ENABLE_IF_IMPL(N, ...) \ | |
typename ::std::enable_if<(__VA_ARGS__), ::cometa::details::unique_enum<N>>::type = \ | |
::cometa::details::unique_enum<N>::value | |
#define CMT_ENABLE_IF(...) CMT_ENABLE_IF_IMPL(__LINE__, __VA_ARGS__) | |
} | |
template <typename T> | |
struct is_enabled : details::is_enabled_impl<T> | |
{ | |
}; | |
template <typename Fn, typename... Args> | |
struct is_callable : details::is_callable_impl<Fn, ctypes_t<Args...>> | |
{ | |
}; | |
template <typename Ret, typename T> | |
struct is_returning_type : details::is_returning_type_impl<Ret, T> | |
{ | |
}; | |
namespace details | |
{ | |
template <typename Fn, CMT_ENABLE_IF(is_callable<Fn()>())> | |
inline auto call_if_callable(Fn&& fn) | |
{ | |
return fn(); | |
} | |
template <typename Fn, CMT_ENABLE_IF(!is_callable<Fn()>())> | |
inline auto call_if_callable(Fn&& fn) | |
{ | |
return std::forward<Fn>(fn); | |
} | |
} | |
template <typename Fn, typename... Args> | |
inline auto bind_func(Fn&& fn, Args&&... args) | |
{ | |
return [=]() CMT_INLINE_LAMBDA { return fn(details::call_if_callable(std::forward<Args>(args))...); }; | |
} | |
template <typename T> | |
constexpr inline bool is_even(T x) | |
{ | |
return (x % 2) == 0; | |
} | |
template <typename T> | |
constexpr inline bool is_odd(T x) | |
{ | |
return !is_even(x); | |
} | |
template <typename T> | |
constexpr inline bool is_poweroftwo(T x) | |
{ | |
return ((x != 0) && !(x & (x - 1))); | |
} | |
template <typename T> | |
constexpr inline unsigned ilog2(T n, unsigned p = 0) | |
{ | |
return (n <= 1) ? p : ilog2(n / 2, p + 1); | |
} | |
template <typename T> | |
constexpr inline T next_poweroftwo(T n) | |
{ | |
return n > 2 ? T(1) << (ilog2(n - 1) + 1) : n; | |
} | |
template <typename T> | |
constexpr inline T prev_poweroftwo(T n) | |
{ | |
return n > 2 ? T(1) << (ilog2(n)) : n; | |
} | |
template <typename T> | |
constexpr inline bool is_divisible(T x, T divisor) | |
{ | |
return x % divisor == 0; | |
} | |
template <typename T> | |
constexpr inline T gcd(T a) | |
{ | |
return a; | |
} | |
template <typename T> | |
constexpr inline T gcd(T a, T b) | |
{ | |
return a < b ? gcd(b, a) : ((a % b == 0) ? b : gcd(b, a % b)); | |
} | |
template <typename T, typename... Ts> | |
constexpr inline T gcd(T a, T b, T c, Ts... rest) | |
{ | |
return gcd(a, gcd(b, c, rest...)); | |
} | |
template <typename T> | |
constexpr inline T lcm(T a) | |
{ | |
return a; | |
} | |
template <typename T> | |
constexpr inline T lcm(T a, T b) | |
{ | |
return a * b / gcd(a, b); | |
} | |
template <typename T, typename... Ts> | |
constexpr inline T lcm(T a, T b, T c, Ts... rest) | |
{ | |
return lcm(a, lcm(b, c, rest...)); | |
} | |
namespace details | |
{ | |
template <int64_t min, int64_t max, typename... Types> | |
struct findinttype_impl | |
{ | |
}; | |
template <int64_t min, int64_t max, typename T, typename... Types> | |
struct findinttype_impl<min, max, T, Types...> | |
{ | |
using type = conditional<(std::numeric_limits<T>::min() <= min && std::numeric_limits<T>::max() >= max), | |
T, typename findinttype_impl<min, max, Types...>::type>; | |
}; | |
template <int64_t min, int64_t max> | |
struct findinttype_impl<min, max> | |
{ | |
using type = void; | |
}; | |
template <typename T> | |
using is_number_impl = | |
std::integral_constant<bool, ((std::is_integral<T>::value) || (std::is_floating_point<T>::value)) && | |
!std::is_same<T, bool>::value>; | |
} | |
template <int64_t min, int64_t max> | |
using findinttype = typename details::findinttype_impl<min, max, uint8_t, int8_t, uint16_t, int16_t, uint32_t, | |
int32_t, uint64_t, int64_t>::type; | |
template <typename T> | |
using is_number = details::is_number_impl<decay<T>>; | |
template <typename... Ts> | |
using is_numbers = and_t<details::is_number_impl<decay<Ts>>...>; | |
namespace details | |
{ | |
template <typename T> | |
struct identity_impl | |
{ | |
using type = T; | |
}; | |
template <typename T> | |
constexpr size_t elementsize = sizeof(T); | |
template <> | |
constexpr size_t elementsize<void> = 1; | |
} | |
template <typename T> | |
using identity = typename details::identity_impl<T>::type; | |
struct swallow | |
{ | |
template <typename... T> | |
CMT_INTRIN constexpr swallow(T&&...) noexcept | |
{ | |
} | |
}; | |
template <typename T, size_t N> | |
struct carray; | |
template <typename T> | |
struct carray<T, 1> | |
{ | |
CMT_INTRIN constexpr carray() noexcept = default; | |
CMT_INTRIN constexpr carray(T val) noexcept : val(val) {} | |
template <typename Fn, size_t index = 0, CMT_ENABLE_IF(is_callable<Fn, csize_t<index>>::value)> | |
CMT_INTRIN constexpr carray(Fn&& fn, csize_t<index> = csize_t<index>{}) noexcept | |
: val(static_cast<T>(fn(csize<index>))) | |
{ | |
} | |
CMT_INTRIN constexpr carray(const carray&) noexcept = default; | |
CMT_INTRIN constexpr carray(carray&&) noexcept = default; | |
CMT_INTRIN static constexpr size_t size() noexcept { return 1; } | |
template <size_t index> | |
CMT_INTRIN constexpr T& get(csize_t<index>) noexcept | |
{ | |
static_assert(index == 0, "carray: Array index is out of range"); | |
return val; | |
} | |
template <size_t index> | |
CMT_INTRIN constexpr const T& get(csize_t<index>) const noexcept | |
{ | |
static_assert(index == 0, "carray: Array index is out of range"); | |
return val; | |
} | |
template <size_t index> | |
CMT_INTRIN constexpr T& get() noexcept | |
{ | |
return get(csize<index>); | |
} | |
template <size_t index> | |
CMT_INTRIN constexpr const T& get() const noexcept | |
{ | |
return get(csize<index>); | |
} | |
CMT_INTRIN constexpr const T* front() const noexcept { return val; } | |
CMT_INTRIN constexpr T* front() noexcept { return val; } | |
CMT_INTRIN constexpr const T* back() const noexcept { return val; } | |
CMT_INTRIN constexpr T* back() noexcept { return val; } | |
CMT_INTRIN constexpr const T* begin() const noexcept { return &val; } | |
CMT_INTRIN constexpr const T* end() const noexcept { return &val + 1; } | |
CMT_INTRIN constexpr T* begin() noexcept { return &val; } | |
CMT_INTRIN constexpr T* end() noexcept { return &val + 1; } | |
CMT_INTRIN constexpr const T* data() const noexcept { return begin(); } | |
CMT_INTRIN constexpr T* data() noexcept { return begin(); } | |
CMT_INTRIN constexpr bool empty() const noexcept { return false; } | |
T val; | |
}; | |
template <typename T, size_t N> | |
struct carray : carray<T, N - 1> | |
{ | |
template <typename... Ts> | |
CMT_INTRIN constexpr carray(T first, Ts... list) noexcept : carray<T, N - 1>(list...), val(first) | |
{ | |
static_assert(sizeof...(list) + 1 == N, "carray: Argument count is invalid"); | |
} | |
template <typename Fn, size_t index = N - 1> | |
CMT_INTRIN constexpr carray(Fn&& fn, csize_t<index> = csize_t<index>{}) noexcept | |
: carray<T, N - 1>(std::forward<Fn>(fn), csize<index - 1>), | |
val(static_cast<T>(fn(csize<index>))) | |
{ | |
} | |
CMT_INTRIN constexpr carray() noexcept = default; | |
CMT_INTRIN constexpr carray(const carray&) noexcept = default; | |
CMT_INTRIN constexpr carray(carray&&) noexcept = default; | |
CMT_INTRIN static constexpr size_t size() noexcept { return N; } | |
CMT_INTRIN constexpr T& get(csize_t<N - 1>) noexcept { return val; } | |
template <size_t index> | |
CMT_INTRIN constexpr T& get(csize_t<index>) noexcept | |
{ | |
return carray<T, N - 1>::get(csize<index>); | |
} | |
CMT_INTRIN constexpr const T& get(csize_t<N - 1>) const noexcept { return val; } | |
template <size_t index> | |
CMT_INTRIN constexpr const T& get(csize_t<index>) const noexcept | |
{ | |
return carray<T, N - 1>::get(csize<index>); | |
} | |
template <size_t index> | |
CMT_INTRIN constexpr T& get() noexcept | |
{ | |
return get(csize<index>); | |
} | |
template <size_t index> | |
CMT_INTRIN constexpr const T& get() const noexcept | |
{ | |
return get(csize<index>); | |
} | |
CMT_INTRIN constexpr const T* front() const noexcept { return carray<T, N - 1>::front(); } | |
CMT_INTRIN constexpr T* front() noexcept { return carray<T, N - 1>::front(); } | |
CMT_INTRIN constexpr const T* back() const noexcept { return val; } | |
CMT_INTRIN constexpr T* back() noexcept { return val; } | |
CMT_INTRIN constexpr const T* begin() const noexcept { return carray<T, N - 1>::begin(); } | |
CMT_INTRIN constexpr const T* end() const noexcept { return &val + 1; } | |
CMT_INTRIN constexpr T* begin() noexcept { return carray<T, N - 1>::begin(); } | |
CMT_INTRIN constexpr T* end() noexcept { return &val + 1; } | |
CMT_INTRIN constexpr const T* data() const noexcept { return begin(); } | |
CMT_INTRIN constexpr T* data() noexcept { return begin(); } | |
CMT_INTRIN constexpr bool empty() const noexcept { return false; } | |
private: | |
T val; | |
}; | |
#define CMT_FN(fn) \ | |
struct fn_##fn \ | |
{ \ | |
template <typename... Args> \ | |
CMT_INLINE_MEMBER decltype(fn(std::declval<Args>()...)) operator()(Args&&... args) const \ | |
{ \ | |
return fn(std::forward<Args>(args)...); \ | |
} \ | |
}; | |
#define CMT_ESC(...) __VA_ARGS__ | |
#define CMT_FN_TPL(tpl_list, tpl_args, fn) \ | |
template <CMT_ESC tpl_list> \ | |
struct fn_##fn \ | |
{ \ | |
template <typename... Args> \ | |
CMT_INLINE_MEMBER decltype(fn<CMT_ESC tpl_args>(std::declval<Args>()...)) operator()( \ | |
Args&&... args) const \ | |
{ \ | |
return fn<CMT_ESC tpl_args>(std::forward<Args>(args)...); \ | |
} \ | |
}; | |
template <typename T> | |
CMT_INTRIN auto pass_through(T&& x) noexcept | |
{ | |
return x; | |
} | |
template <typename... Ts> | |
CMT_INTRIN void noop(Ts...) noexcept | |
{ | |
} | |
template <typename T1, typename... Ts> | |
CMT_INTRIN constexpr T1&& get_first(T1&& x, Ts...) noexcept | |
{ | |
return std::forward<T1>(x); | |
} | |
template <typename T1, typename T2, typename... Ts> | |
CMT_INTRIN constexpr T2&& get_second(T1, T2&& x, Ts...) noexcept | |
{ | |
return std::forward<T2>(x); | |
} | |
template <typename T1, typename T2, typename T3, typename... Ts> | |
CMT_INTRIN constexpr T3&& get_third(T1, T2, T3&& x, Ts...) noexcept | |
{ | |
return std::forward<T3>(x); | |
} | |
template <typename T, typename... Ts> | |
CMT_INTRIN constexpr T returns(Ts...) | |
{ | |
return T(); | |
} | |
CMT_FN(pass_through) | |
CMT_FN(noop) | |
CMT_FN(get_first) | |
CMT_FN(get_second) | |
CMT_FN(get_third) | |
CMT_FN_TPL((typename T), (T), returns) | |
template <typename T1, typename T2> | |
CMT_INTRIN bool is_equal(const T1& x, const T2& y) | |
{ | |
return x == y; | |
} | |
template <typename T1, typename T2> | |
CMT_INTRIN bool is_notequal(const T1& x, const T2& y) | |
{ | |
return x != y; | |
} | |
template <typename T1, typename T2> | |
CMT_INTRIN bool is_less(const T1& x, const T2& y) | |
{ | |
return x < y; | |
} | |
template <typename T1, typename T2> | |
CMT_INTRIN bool is_greater(const T1& x, const T2& y) | |
{ | |
return x > y; | |
} | |
template <typename T1, typename T2> | |
CMT_INTRIN bool is_lessorequal(const T1& x, const T2& y) | |
{ | |
return x <= y; | |
} | |
template <typename T1, typename T2> | |
CMT_INTRIN bool is_greaterorequal(const T1& x, const T2& y) | |
{ | |
return x >= y; | |
} | |
CMT_FN(is_equal) | |
CMT_FN(is_notequal) | |
CMT_FN(is_less) | |
CMT_FN(is_greater) | |
CMT_FN(is_lessorequal) | |
CMT_FN(is_greaterorequal) | |
namespace details | |
{ | |
template <typename, typename = void> | |
struct has_begin_end_impl : std::false_type | |
{ | |
}; | |
template <typename T> | |
struct has_begin_end_impl<T, void_t<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>> | |
: std::true_type | |
{ | |
}; | |
template <typename, typename = void> | |
struct has_value_type_impl : std::false_type | |
{ | |
}; | |
template <typename T> | |
struct has_value_type_impl<T, void_t<typename T::value_type>> : std::true_type | |
{ | |
}; | |
template <typename, typename = void> | |
struct has_data_size_impl : std::false_type | |
{ | |
}; | |
template <typename T> | |
struct has_data_size_impl<T, void_t<decltype(std::declval<T>().size()), decltype(std::declval<T>().data())>> | |
: std::true_type | |
{ | |
}; | |
template <typename, typename Fallback, typename = void> | |
struct value_type_impl | |
{ | |
using type = Fallback; | |
}; | |
template <typename T, typename Fallback> | |
struct value_type_impl<T, Fallback, void_t<typename T::value_type>> | |
{ | |
using type = typename T::value_type; | |
}; | |
} | |
template <typename T> | |
using has_begin_end = details::has_begin_end_impl<decay<T>>; | |
template <typename T> | |
using has_data_size = details::has_data_size_impl<decay<T>>; | |
template <typename T> | |
using value_type_of = typename decay<T>::value_type; | |
template <typename T, T... values, typename Fn> | |
CMT_INTRIN void cforeach(cvals_t<T, values...>, Fn&& fn) | |
{ | |
swallow{ (fn(cval<T, values>), void(), 0)... }; | |
} | |
template <typename T, typename Fn, CMT_ENABLE_IF(has_begin_end<T>::value)> | |
CMT_INTRIN void cforeach(T&& list, Fn&& fn) | |
{ | |
for (const auto& v : list) | |
{ | |
fn(v); | |
} | |
} | |
template <typename T, size_t N, typename Fn> | |
CMT_INTRIN void cforeach(const T (&array)[N], Fn&& fn) | |
{ | |
for (size_t i = 0; i < N; i++) | |
{ | |
fn(array[i]); | |
} | |
} | |
namespace details | |
{ | |
template <typename... Ts, typename Fn, size_t... indices> | |
CMT_INTRIN void cforeach_tuple_impl(const std::tuple<Ts...>& tuple, Fn&& fn, csizes_t<indices...>) | |
{ | |
swallow{ (fn(std::get<indices>(tuple)), void(), 0)... }; | |
} | |
template <size_t index, typename... types> | |
CMT_INTRIN auto get_type_arg(ctypes_t<types...> type_list) | |
{ | |
return ctype<type_of<details::get_nth_type<index, types...>>>; | |
} | |
template <typename T0, typename... types, typename Fn, size_t... indices> | |
CMT_INTRIN void cforeach_types_impl(ctypes_t<T0, types...> type_list, Fn&& fn, csizes_t<indices...>) | |
{ | |
swallow{ (fn(get_type_arg<indices>(type_list)), void(), 0)... }; | |
} | |
} | |
template <typename... Ts, typename Fn> | |
CMT_INTRIN void cforeach(ctypes_t<Ts...> types, Fn&& fn) | |
{ | |
details::cforeach_types_impl(types, std::forward<Fn>(fn), csizeseq<sizeof...(Ts)>); | |
} | |
template <typename... Ts, typename Fn> | |
CMT_INTRIN void cforeach(const std::tuple<Ts...>& tuple, Fn&& fn) | |
{ | |
details::cforeach_tuple_impl(tuple, std::forward<Fn>(fn), csizeseq<sizeof...(Ts)>); | |
} | |
template <typename A0, typename A1, typename Fn> | |
CMT_INTRIN void cforeach(A0&& a0, A1&& a1, Fn&& fn) | |
{ | |
cforeach(std::forward<A0>(a0), | |
[&](auto v0) { cforeach(std::forward<A1>(a1), [&](auto v1) { fn(v0, v1); }); }); | |
} | |
template <typename A0, typename A1, typename A2, typename Fn> | |
CMT_INTRIN void cforeach(A0&& a0, A1&& a1, A2&& a2, Fn&& fn) | |
{ | |
cforeach(std::forward<A0>(a0), [&](auto v0) { | |
cforeach(std::forward<A1>(a1), | |
[&](auto v1) { cforeach(std::forward<A2>(a2), [&](auto v2) { fn(v0, v1, v2); }); }); | |
}); | |
} | |
template <typename TrueFn, typename FalseFn = fn_noop> | |
CMT_INTRIN decltype(auto) cif(cbool_t<true>, TrueFn&& truefn, FalseFn&& = FalseFn()) | |
{ | |
return truefn(cbool<true>); | |
} | |
template <typename TrueFn, typename FalseFn = fn_noop> | |
CMT_INTRIN decltype(auto) cif(cbool_t<false>, TrueFn&&, FalseFn&& falsefn = FalseFn()) | |
{ | |
return falsefn(cbool<false>); | |
} | |
template <typename T, T start, T stop, typename BodyFn> | |
CMT_INTRIN decltype(auto) cfor(cval_t<T, start>, cval_t<T, stop>, BodyFn&& bodyfn) | |
{ | |
return cforeach(cvalrange<T, start, stop>, std::forward<BodyFn>(bodyfn)); | |
} | |
template <typename T, T... vs, typename U, typename Function, typename Fallback = fn_noop> | |
void cswitch(cvals_t<T, vs...>, const U& value, Function&& function, Fallback&& fallback = Fallback()) | |
{ | |
bool result = false; | |
swallow{ (result = result || ((vs == value) ? (function(cval<T, vs>), void(), true) : false), void(), | |
0)... }; | |
if (!result) | |
fallback(); | |
} | |
template <typename T, typename Fn, typename DefFn = fn_noop, typename CmpFn = fn_is_equal> | |
CMT_INTRIN decltype(auto) cswitch(cvals_t<T>, identity<T>, Fn&&, DefFn&& deffn = DefFn(), CmpFn&& = CmpFn()) | |
{ | |
return deffn(); | |
} | |
template <typename T, T v0, T... values, typename Fn, typename DefFn = fn_noop, typename CmpFn = fn_is_equal> | |
CMT_INTRIN decltype(auto) cswitch(cvals_t<T, v0, values...>, identity<T> value, Fn&& fn, | |
DefFn&& deffn = DefFn(), CmpFn&& cmpfn = CmpFn()) | |
{ | |
if (cmpfn(value, v0)) | |
{ | |
return fn(cval<T, v0>); | |
} | |
else | |
{ | |
return cswitch(cvals_t<T, values...>(), value, std::forward<Fn>(fn), std::forward<DefFn>(deffn), | |
std::forward<CmpFn>(cmpfn)); | |
} | |
} | |
namespace details | |
{ | |
template <typename T, typename Fn1, typename Fn2, typename... Fns> | |
inline decltype(auto) cmatch_impl(T&& value, Fn1&& first, Fn2&& second, Fns&&... rest); | |
template <typename T, typename Fn, typename... Ts> | |
inline decltype(auto) cmatch_impl(T&& value, Fn&& last); | |
template <typename T, typename Fn, typename... Fns> | |
inline decltype(auto) cmatch_impl2(cbool_t<true>, T&& value, Fn&& fn, Fns&&...) | |
{ | |
return fn(std::forward<T>(value)); | |
} | |
template <typename T, typename Fn, typename... Fns> | |
inline decltype(auto) cmatch_impl2(cbool_t<false>, T&& value, Fn&&, Fns&&... rest) | |
{ | |
return cmatch_impl(std::forward<T>(value), std::forward<Fns>(rest)...); | |
} | |
template <typename T, typename Fn1, typename Fn2, typename... Fns> | |
inline decltype(auto) cmatch_impl(T&& value, Fn1&& first, Fn2&& second, Fns&&... rest) | |
{ | |
using first_arg = typename function_arguments<Fn1>::template nth<0>; | |
constexpr bool is_same = std::is_same<decay<T>, decay<first_arg>>::value; | |
return cmatch_impl2(cbool<is_same>, std::forward<T>(value), std::forward<Fn1>(first), | |
std::forward<Fn2>(second), std::forward<Fns>(rest)...); | |
} | |
template <typename T, typename Fn, typename... Ts> | |
inline decltype(auto) cmatch_impl(T&& value, Fn&& last) | |
{ | |
return last(std::forward<T>(value)); | |
} | |
} | |
template <typename T, typename Fn, typename... Args> | |
inline decltype(auto) cmatch(T&& value, Fn&& fn, Args... args) | |
{ | |
return details::cmatch_impl(std::forward<T>(value), std::forward<Fn>(fn), std::forward<Args>(args)...); | |
} | |
namespace details | |
{ | |
template <typename Result, typename... Args> | |
struct virtual_function | |
{ | |
virtual Result operator()(Args... args) = 0; | |
virtual virtual_function* make_copy() const = 0; | |
CMT_INTRIN virtual ~virtual_function() = default; | |
}; | |
template <typename Fn, typename Result, typename... Args> | |
struct virtual_function_impl : virtual_function<Result, Args...> | |
{ | |
public: | |
CMT_INTRIN virtual_function_impl(const Fn& fn) : fn(fn) {} | |
CMT_INTRIN Result operator()(Args... args) override final { return fn(args...); } | |
CMT_INTRIN virtual_function<Result, Args...>* make_copy() const override final | |
{ | |
return new virtual_function_impl{ fn }; | |
} | |
CMT_INTRIN ~virtual_function_impl() {} | |
private: | |
Fn fn; | |
}; | |
template <typename Fn> | |
struct func_filter | |
{ | |
typedef Fn type; | |
}; | |
template <typename Result, typename... Args> | |
struct func_filter<Result(Args...)> | |
{ | |
typedef Result (*type)(Args...); | |
}; | |
template <typename T> | |
constexpr CMT_INTRIN T return_val() noexcept | |
{ | |
return {}; | |
} | |
template <> | |
constexpr CMT_INTRIN void return_val<void>() noexcept | |
{ | |
} | |
} | |
template <typename> | |
struct function; | |
/** | |
* @brief std::function-like lightweight function wrapper | |
* @code | |
* function<int( float )> f = []( float x ){ return static_cast<int>( x ); }; | |
* CHECK( f( 3.4f ) == 3 ) | |
* @encode | |
*/ | |
template <typename Result, typename... Args> | |
struct function<Result(Args...)> | |
{ | |
using this_t = function<Result(Args...)>; | |
function(function&& other) : fn(other.fn) { other.fn = nullptr; } | |
function& operator=(function&& other) | |
{ | |
fn = other.fn; | |
other.fn = nullptr; | |
return *this; | |
} | |
CMT_INTRIN function() : fn(nullptr) {} | |
CMT_INTRIN function(std::nullptr_t) : fn(nullptr) {} | |
template <typename Func> | |
CMT_INTRIN function(const Func& x) | |
: fn(new details::virtual_function_impl<typename details::func_filter<Func>::type, Result, Args...>( | |
x)) | |
{ | |
} | |
function(const this_t& other) : fn(other.fn ? other.fn->make_copy() : nullptr) {} | |
CMT_INTRIN function& operator=(const this_t& other) | |
{ | |
if ((&other != this) && (other.fn)) | |
{ | |
auto* temp = other.fn->make_copy(); | |
delete fn; | |
fn = temp; | |
} | |
return *this; | |
} | |
CMT_INTRIN function& operator=(std::nullptr_t) | |
{ | |
delete fn; | |
fn = nullptr; | |
return *this; | |
} | |
template <typename Fn> | |
CMT_INTRIN function& operator=(const Fn& x) | |
{ | |
using FnImpl = | |
details::virtual_function_impl<typename details::func_filter<Fn>::type, Result, Args...>; | |
FnImpl* temp = new FnImpl(x); | |
delete fn; | |
fn = temp; | |
return *this; | |
} | |
CMT_INTRIN Result operator()(Args... args) const | |
{ | |
if (fn) | |
return (*fn)(args...); | |
else | |
return details::return_val<Result>(); | |
} | |
CMT_INTRIN explicit operator bool() const noexcept { return !!fn; } | |
CMT_INTRIN ~function() { delete fn; } | |
private: | |
details::virtual_function<Result, Args...>* fn; | |
}; | |
template <typename Ret, typename... Args, typename T, typename Fn, typename DefFn = fn_noop> | |
CMT_INLINE function<Ret(Args...)> cdispatch(cvals_t<T>, identity<T>, Fn&&, DefFn&& deffn = DefFn()) | |
{ | |
return [=](Args... args) CMT_INLINE_MEMBER -> Ret { return deffn(std::forward<Args>(args)...); }; | |
} | |
template <typename Ret, typename... Args, typename T, T v0, T... values, typename Fn, | |
typename DefFn = fn_noop> | |
inline function<Ret(Args...)> cdispatch(cvals_t<T, v0, values...>, identity<T> value, Fn&& fn, | |
DefFn&& deffn = DefFn()) | |
{ | |
if (value == v0) | |
{ | |
return [=](Args... args) | |
CMT_INLINE_MEMBER -> Ret { return fn(cval<T, v0>, std::forward<Args>(args)...); }; | |
} | |
else | |
{ | |
return cdispatch<Ret, Args...>(cvals_t<T, values...>(), value, std::forward<Fn>(fn), | |
std::forward<DefFn>(deffn)); | |
} | |
} | |
template <typename T, T... values> | |
inline size_t cfind(cvals_t<T, values...>, identity<T> value) | |
{ | |
static const T temp[] = { values... }; | |
return static_cast<size_t>( | |
std::distance(std::begin(temp), std::find(std::begin(temp), std::end(temp), value))); | |
} | |
template <typename Fn, typename... Args> | |
CMT_NOINLINE static result_of<Fn(Args...)> noinline(Fn&& fn, Args&&... args) | |
{ | |
return fn(std::forward<Args>(args)...); | |
} | |
template <typename Fn> | |
struct fn_noinline | |
{ | |
template <typename... Args> | |
CMT_INTRIN result_of<Fn(Args...)> operator()(Args&&... args) const | |
{ | |
return noinline(Fn{}, std::forward<Args>(args)...); | |
} | |
}; | |
template <typename... Args, typename Fn, typename Ret = decltype(std::declval<Fn>()(std::declval<Args>()...)), | |
typename NonMemFn = Ret (*)(Fn*, Args...)> | |
CMT_INTRIN NonMemFn make_nonmember(const Fn&) | |
{ | |
return [](Fn* fn, Args... args) -> Ret { return fn->operator()(std::forward<Args>(args)...); }; | |
} | |
template <typename T> | |
struct array_ref | |
{ | |
public: | |
using value_type = T; | |
using pointer = value_type*; | |
using const_pointer = const value_type*; | |
using reference = value_type&; | |
using const_reference = const value_type&; | |
using iterator = pointer; | |
using const_iterator = const_pointer; | |
using reverse_iterator = std::reverse_iterator<pointer>; | |
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |
using size_type = std::size_t; | |
using difference_type = std::ptrdiff_t; | |
constexpr array_ref() noexcept : m_data(nullptr), m_size(0) {} | |
constexpr array_ref(const array_ref&) noexcept = default; | |
constexpr array_ref(array_ref&&) noexcept = default; | |
constexpr array_ref& operator=(const array_ref&) noexcept = default; | |
constexpr array_ref& operator=(array_ref&&) noexcept = default; | |
template <size_t N> | |
constexpr array_ref(value_type (&arr)[N]) noexcept : m_data(arr), m_size(N) | |
{ | |
} | |
template <size_t N> | |
constexpr array_ref(const std::array<T, N>& arr) noexcept : m_data(arr.data()), m_size(N) | |
{ | |
} | |
template <size_t N> | |
constexpr array_ref(std::array<T, N>& arr) noexcept : m_data(arr.data()), m_size(N) | |
{ | |
} | |
template <typename... Ts> | |
constexpr array_ref(const std::vector<T, Ts...>& vec) noexcept : m_data(vec.data()), m_size(vec.size()) | |
{ | |
} | |
template <typename... Ts, CMT_ENABLE_IF(sizeof...(Ts), is_const<T>::value)> | |
constexpr array_ref(const std::vector<remove_const<T>, Ts...>& vec) noexcept : m_data(vec.data()), | |
m_size(vec.size()) | |
{ | |
} | |
template <typename... Ts> | |
constexpr array_ref(std::vector<T, Ts...>& vec) noexcept : m_data(vec.data()), m_size(vec.size()) | |
{ | |
} | |
template <typename InputIter> | |
constexpr array_ref(InputIter first, InputIter last) noexcept : m_data(std::addressof(*first)), | |
m_size(std::distance(first, last)) | |
{ | |
} | |
constexpr array_ref(T* data, size_type size) noexcept : m_data(data), m_size(size) {} | |
constexpr reference front() const noexcept { return m_data[0]; } | |
constexpr reference back() const noexcept { return m_data[m_size - 1]; } | |
constexpr iterator begin() const noexcept { return m_data; } | |
constexpr iterator end() const noexcept { return m_data + m_size; } | |
constexpr const_iterator cbegin() const noexcept { return m_data; } | |
constexpr const_iterator cend() const noexcept { return m_data + m_size; } | |
constexpr pointer data() const noexcept { return m_data; } | |
constexpr std::size_t size() const noexcept { return m_size; } | |
constexpr bool empty() const noexcept { return !m_size; } | |
constexpr reference operator[](std::size_t index) const { return m_data[index]; } | |
private: | |
pointer m_data; | |
size_type m_size; | |
}; | |
template <typename T> | |
constexpr inline T choose_const() | |
{ | |
static_assert(sizeof(T) != 0, "T not found in the list of template arguments"); | |
return T(); | |
} | |
/** | |
* Selects constant of the specific type | |
* @code | |
* CHECK( choose_const<f32>( 32.0f, 64.0 ) == 32.0f ); | |
* CHECK( choose_const<f64>( 32.0f, 64.0 ) == 64.0 ); | |
* @endcode | |
*/ | |
template <typename T, typename C1, typename... Cs> | |
constexpr inline T choose_const(C1 c1, Cs... constants) | |
{ | |
return std::is_same<T, C1>::value ? static_cast<T>(c1) : choose_const<T>(constants...); | |
} | |
template <typename T, std::size_t size> | |
inline array_ref<T> make_array_ref(T (&data)[size]) | |
{ | |
return array_ref<T>(data); | |
} | |
template <typename T> | |
inline array_ref<T> make_array_ref(T* data, std::size_t size) | |
{ | |
return array_ref<T>(data, data + size); | |
} | |
template <typename Container, CMT_ENABLE_IF(has_data_size<Container>::value), | |
typename T = remove_pointer<decltype(std::declval<Container>().data())>> | |
inline array_ref<T> make_array_ref(Container& cont) | |
{ | |
return array_ref<T>(cont.data(), cont.size()); | |
} | |
template <typename Container, CMT_ENABLE_IF(has_data_size<Container>::value), | |
typename T = remove_pointer<decltype(std::declval<Container>().data())>> | |
inline array_ref<const T> make_array_ref(const Container& cont) | |
{ | |
return array_ref<const T>(cont.data(), cont.size()); | |
} | |
template <typename T> | |
inline array_ref<T> make_array_ref(std::vector<T>& cont) | |
{ | |
return array_ref<T>(cont.data(), cont.size()); | |
} | |
template <typename T> | |
inline array_ref<const T> make_array_ref(const std::vector<T>& cont) | |
{ | |
return array_ref<const T>(cont.data(), cont.size()); | |
} | |
template <typename Type, typename ErrEnum, ErrEnum OkValue = static_cast<ErrEnum>(0)> | |
struct result | |
{ | |
using value_type = Type; | |
using reference = value_type&; | |
using const_reference = const value_type&; | |
using pointer = value_type*; | |
using const_pointer = const value_type*; | |
using error_type = ErrEnum; | |
constexpr static error_type ok_value = OkValue; | |
constexpr result(const result&) = default; | |
constexpr result(result&&) noexcept = default; | |
constexpr result(ErrEnum error) noexcept : m_error(error) {} | |
template <typename ValueInit, CMT_ENABLE_IF(std::is_constructible<value_type, ValueInit>::value)> | |
constexpr result(ValueInit&& value) noexcept : m_value(std::forward<ValueInit>(value)), m_error(OkValue) | |
{ | |
} | |
constexpr result(const Type& value) noexcept : m_value(value), m_error(OkValue) {} | |
constexpr result(Type&& value) noexcept : m_value(std::move(value)), m_error(OkValue) {} | |
constexpr explicit operator bool() const { return m_error == OkValue; } | |
constexpr const_reference operator*() const { return m_value; } | |
constexpr reference operator*() { return m_value; } | |
constexpr const_pointer operator->() const { return &m_value; } | |
constexpr pointer operator->() { return &m_value; } | |
constexpr const_reference value() const { return m_value; } | |
constexpr reference value() { return m_value; } | |
constexpr ErrEnum error() const { return m_error; } | |
constexpr bool ok() const { return m_error == OkValue; } | |
private: | |
Type m_value; | |
ErrEnum m_error; | |
}; | |
template <typename Tfrom> | |
struct autocast_impl | |
{ | |
const Tfrom value; | |
template <typename T> | |
CMT_INTRIN constexpr operator T() const noexcept | |
{ | |
return static_cast<T>(value); | |
} | |
}; | |
template <typename Tfrom> | |
CMT_INTRIN constexpr autocast_impl<Tfrom> autocast(const Tfrom& value) noexcept | |
{ | |
return { value }; | |
} | |
inline void stop_constexpr() {} | |
namespace details | |
{ | |
template <typename T, typename = void> | |
struct signed_type_impl | |
{ | |
using type = T; | |
}; | |
template <typename T> | |
struct signed_type_impl<T, void_t<enable_if<std::is_unsigned<T>::value>>> | |
{ | |
using type = findinttype<std::numeric_limits<T>::min(), std::numeric_limits<T>::max()>; | |
}; | |
} | |
template <typename T> | |
using signed_type = typename details::signed_type_impl<T>::type; | |
template <typename T> | |
struct range | |
{ | |
using value_type = T; | |
using reference = T&; | |
using const_reference = const T&; | |
using pointer = T*; | |
using const_pointer = const T*; | |
using diff_type = decltype(std::declval<T>() - std::declval<T>()); | |
constexpr range(value_type begin, value_type end, diff_type step) noexcept : value_begin(begin), | |
value_end(end), | |
step(step) | |
{ | |
} | |
struct iterator | |
{ | |
value_type value; | |
diff_type step; | |
const_reference operator*() const { return value; } | |
const_pointer operator->() const { return &value; } | |
iterator& operator++() | |
{ | |
value += step; | |
return *this; | |
} | |
iterator operator++(int) | |
{ | |
iterator copy = *this; | |
++(*this); | |
return copy; | |
} | |
bool operator!=(const iterator& other) const | |
{ | |
return step > 0 ? value < other.value : value > other.value; | |
} | |
}; | |
value_type value_begin; | |
value_type value_end; | |
diff_type step; | |
iterator begin() const { return iterator{ value_begin, step }; } | |
iterator end() const { return iterator{ value_end, step }; } | |
}; | |
template <typename T> | |
range<T> make_range(T begin, T end) | |
{ | |
return range<T>(begin, end, end > begin ? 1 : -1); | |
} | |
template <typename T, typename diff_type = decltype(std::declval<T>() - std::declval<T>())> | |
range<T> make_range(T begin, T end, diff_type step) | |
{ | |
return range<T>(begin, end, step); | |
} | |
template <typename T> | |
struct named_arg | |
{ | |
T value; | |
const char* name; | |
}; | |
struct named | |
{ | |
constexpr named(const char* name) noexcept : name(name) {} | |
template <typename T> | |
CMT_INTRIN constexpr named_arg<T> operator=(T&& value) | |
{ | |
return named_arg<T>{ std::forward<T>(value), name }; | |
} | |
const char* name; | |
}; | |
inline named operator""_arg(const char* name, size_t) { return name; } | |
template <size_t N> | |
struct cstring | |
{ | |
using value_type = char; | |
using size_type = size_t; | |
constexpr const value_type* c_str() const noexcept { return value; } | |
constexpr const value_type* data() const noexcept { return value; } | |
const value_type value[N]; | |
constexpr size_type length() const noexcept { return N - 1; } | |
constexpr size_type size() const noexcept { return N; } | |
constexpr friend bool operator==(const cstring& left, const cstring& right) noexcept | |
{ | |
for (size_t i = 0; i < 1; i++) | |
if (left.value[i] != right.value[i]) | |
return false; | |
return true; | |
} | |
constexpr friend bool operator!=(const cstring& left, const cstring& right) noexcept | |
{ | |
return !(left == right); | |
} | |
template <size_t NN> | |
constexpr bool operator==(const cstring<NN>& other) const noexcept | |
{ | |
return false; | |
} | |
template <size_t NN> | |
constexpr bool operator!=(const cstring<NN>& other) const noexcept | |
{ | |
return true; | |
} | |
constexpr char operator[](size_t index) const noexcept { return value[index]; } | |
}; | |
namespace details | |
{ | |
template <size_t N, size_t... indices> | |
CMT_INLINE constexpr cstring<N> make_cstring_impl(const char (&str)[N], csizes_t<indices...>) | |
{ | |
return { { str[indices]..., 0 } }; | |
} | |
template <size_t N1, size_t N2, size_t... indices> | |
CMT_INLINE constexpr cstring<N1 - 1 + N2 - 1 + 1> concat_str_impl(const cstring<N1>& str1, | |
const cstring<N2>& str2, | |
csizes_t<indices...>) | |
{ | |
constexpr size_t L1 = N1 - 1; | |
return { { (indices < L1 ? str1[indices] : str2[indices - L1])..., 0 } }; | |
} | |
template <size_t N1, size_t N2, typename... Args> | |
CMT_INLINE constexpr cstring<N1 - 1 + N2 - 1 + 1> concat_str_impl(const cstring<N1>& str1, | |
const cstring<N2>& str2) | |
{ | |
return concat_str_impl(str1, str2, csizeseq<N1 - 1 + N2 - 1>); | |
} | |
template <size_t N1, size_t Nfrom, size_t Nto, size_t... indices> | |
CMT_INTRIN cstring<N1 - Nfrom + Nto> str_replace_impl(size_t pos, const cstring<N1>& str, | |
const cstring<Nfrom>&, const cstring<Nto>& to, | |
csizes_t<indices...>) | |
{ | |
if (pos == size_t(-1)) | |
stop_constexpr(); | |
return { { (indices < pos ? str[indices] : (indices < pos + Nto - 1) ? to[indices - pos] | |
: str[indices - Nto + Nfrom])..., | |
0 } }; | |
} | |
} | |
CMT_INTRIN constexpr cstring<1> concat_cstring() { return { { 0 } }; } | |
template <size_t N1> | |
CMT_INTRIN constexpr cstring<N1> concat_cstring(const cstring<N1>& str1) | |
{ | |
return str1; | |
} | |
template <size_t N1, size_t N2, typename... Args> | |
CMT_INTRIN constexpr auto concat_cstring(const cstring<N1>& str1, const cstring<N2>& str2, | |
const Args&... args) | |
{ | |
return details::concat_str_impl(str1, concat_cstring(str2, args...)); | |
} | |
template <size_t N> | |
CMT_INTRIN constexpr cstring<N> make_cstring(const char (&str)[N]) | |
{ | |
return details::make_cstring_impl(str, csizeseq<N - 1>); | |
} | |
template <char... chars> | |
CMT_INTRIN constexpr cstring<sizeof...(chars) + 1> make_cstring(cchars_t<chars...>) | |
{ | |
return { { chars..., 0 } }; | |
} | |
template <size_t N1, size_t Nneedle> | |
CMT_INTRIN size_t str_find(const cstring<N1>& str, const cstring<Nneedle>& needle) | |
{ | |
size_t count = 0; | |
for (size_t i = 0; i < N1; i++) | |
{ | |
if (str[i] == needle[count]) | |
count++; | |
else | |
count = 0; | |
if (count == Nneedle - 1) | |
return i + 1 - (Nneedle - 1); | |
} | |
return size_t(-1); | |
} | |
template <size_t N1, size_t Nfrom, size_t Nto> | |
CMT_INTRIN cstring<N1 - Nfrom + Nto> str_replace(const cstring<N1>& str, const cstring<Nfrom>& from, | |
const cstring<Nto>& to) | |
{ | |
return details::str_replace_impl(str_find(str, from), str, from, to, csizeseq<N1 - Nfrom + Nto - 1>); | |
} | |
using pconstvoid = const void*; | |
struct type_id_t | |
{ | |
constexpr type_id_t(const void* id) noexcept : id(id) {} | |
constexpr bool operator==(type_id_t other) const { return id == other.id; } | |
constexpr bool operator!=(type_id_t other) const { return !(id == other.id); } | |
const void* const id; | |
}; | |
namespace details | |
{ | |
constexpr inline size_t strlen(const char* str) { return *str ? 1 + cometa::details::strlen(str + 1) : 0; } | |
template <typename T> | |
constexpr inline type_id_t typeident_impl() noexcept | |
{ | |
return type_id_t(pconstvoid(&typeident_impl<T>)); | |
} | |
#ifdef CMT_COMPILER_CLANG | |
constexpr size_t typename_prefix = sizeof("auto cometa::ctype_name() [T = ") - 1; | |
constexpr size_t typename_postfix = sizeof("]") - 1; | |
#else | |
constexpr size_t typename_prefix = sizeof("constexpr auto cometa::ctype_name() [with T = ") - 1; | |
constexpr size_t typename_postfix = sizeof("]") - 1; | |
#endif | |
template <size_t... indices, size_t Nout = 1 + sizeof...(indices)> | |
constexpr cstring<Nout> gettypename_impl(const char* str, csizes_t<indices...>) noexcept | |
{ | |
return cstring<Nout>{ (str[indices])..., 0 }; | |
} | |
} | |
template <typename T> | |
constexpr auto ctype_name() noexcept | |
{ | |
constexpr size_t length = | |
sizeof(CMT_FUNC_SIGNATURE) - 1 - details::typename_prefix - details::typename_postfix; | |
return details::gettypename_impl(CMT_FUNC_SIGNATURE + details::typename_prefix, csizeseq<length>); | |
} | |
/** | |
* @brief Gets the fully qualified name of the type, including namespace and | |
* template parameters (if any) | |
* @tparam T type | |
* @return name of the type | |
*/ | |
template <typename T> | |
inline const char* type_name() noexcept | |
{ | |
static const auto name = ctype_name<T>(); | |
return name.c_str(); | |
} | |
/** | |
* @brief Gets the fully qualified name of the type, including namespace and | |
* template parameters (if any) | |
* @param x value of specific type | |
* @return name of the type | |
*/ | |
template <typename T> | |
inline const char* type_name(T x) noexcept | |
{ | |
(void)x; | |
return type_name<T>(); | |
} | |
/** | |
* @brief Gets unique value associated with the type | |
* @tparam T type | |
* @return value of type that supports operator== and operator!= | |
*/ | |
template <typename T> | |
constexpr inline type_id_t ctypeid() | |
{ | |
return details::typeident_impl<T>(); | |
} | |
/** | |
* @brief Gets unique value associated with the type | |
* @param x value of specific type | |
* @return value of type that supports operator== and operator!= | |
*/ | |
template <typename T> | |
constexpr inline type_id_t ctypeid(T x) | |
{ | |
(void)x; | |
return details::typeident_impl<T>(); | |
} | |
} | |
#pragma gcc diagnostic pop |