/* ## Advanced C++ Topics This is an overview of some of the more obscure C++ techniques used in this project. This assumes you're already familiar with templates in C++. ### Template recursion [TODO] ### Almost perfect template forwarding This is adapted from https://akrzemi1.wordpress.com/2013/10/10/too-perfect-forwarding/ Suppose you have a inner class, with a couple of constructors: ``` class InnerClass { public: InnerClass(int arg1, int arg2); InnerClass(int arg1); InnerClass(); }; ``` Now you want to create a wrapper class. This wrapper class should provide the exact same constructors as `InnerClass`, so you use perfect forwarding: ``` class WrapperClass { public: template WrapperClass(Args&& ... args) : inner_object(std::forward(args)...) {} InnerClass inner_object; }; ``` Now you can almost use the wrapper class as expected, but only almost: ``` void make_wrappers(void) { WrapperClass wrapper1; // ok, maps to InnerClass() WrapperClass wrapper2(1); // ok, maps to InnerClass(int arg1) WrapperClass wrapper3(1,2); // ok, maps to InnerClass(int arg1, arg2) WrapperClass wrapper4 = wrapper1; // does not compile } ``` The last assignment fails. What _you_ obviously wanted, is to use the copy constructor of WrapperClass. However the compiler will use the perfect forwarding constructor of WrapperClass for this assignment. So after template expansion it would try to use the following constructor: ``` WrapperClass(InnerClass& arg) : inner_object(arg) {} ``` Clearly this is not what we wanted and in this case it will fail because the exists no constructor of the form `InnerClass(WrapperClass& arg)`. And thus we need to make the perfect forwarding a little less perfect, by telling it "only enable this constructor if the first argument of the argument list is not of type WrapperClass". The modified version thus looks like this: ``` class WrapperClass { public: template::template first_is_not())> WrapperClass(Args&& ... args) : inner_object(std::forward(args)...) {} InnerClass inner_object; }; ``` */ #ifndef __CPP_UTILS_HPP #define __CPP_UTILS_HPP #include #include #include #include #include //#include /* Backport features from C++14 and C++17 ------------------------------------*/ #if __cplusplus < 201402L namespace std { template< class T > using underlying_type_t = typename underlying_type::type; // source: http://en.cppreference.com/w/cpp/types/enable_if template< bool B, class T = void > using enable_if_t = typename enable_if::type; // source: https://en.cppreference.com/w/cpp/types/conditional template< bool B, class T, class F > using conditional_t = typename conditional::type; // source: http://en.cppreference.com/w/cpp/utility/tuple/tuple_element template using tuple_element_t = typename tuple_element::type; // source: https://en.cppreference.com/w/cpp/types/remove_cv template< class T > using remove_cv_t = typename remove_cv::type; template< class T > using remove_const_t = typename remove_const::type; template< class T > using remove_volatile_t = typename remove_volatile::type; template< class T > using remove_reference_t = typename remove_reference::type; template< class T > using decay_t = typename decay::type; // integer_sequence implementation adapted from // https://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence /// Class template integer_sequence template struct integer_sequence { using type = integer_sequence; typedef _Tp value_type; static constexpr size_t size() noexcept { return sizeof...(_Idx); } }; template struct _merge_and_renumber; template struct _merge_and_renumber, integer_sequence<_Tp, I2...>> : integer_sequence<_Tp, I1..., (sizeof...(I1)+I2)...> { }; template struct make_integer_sequence : _merge_and_renumber::type, typename make_integer_sequence<_Tp, N - N/2>::type> { }; template struct make_integer_sequence<_Tp, 0> : integer_sequence<_Tp> { }; template struct make_integer_sequence<_Tp, 1> : integer_sequence<_Tp, 0> { }; /// Alias template index_sequence template using index_sequence = integer_sequence; /// Alias template make_index_sequence template using make_index_sequence = typename make_integer_sequence::type; } #endif namespace fibre { // Creates the index sequence { IFrom, IFrom + 1, IFrom + 2, ..., ITo - 1 } template struct make_integer_sequence_from_to_impl { using type = typename make_integer_sequence_from_to_impl<_Tp, IFrom, ITo - 1, ITo - 1, I...>::type; }; template struct make_integer_sequence_from_to_impl<_Tp, IFrom, IFrom, I...> { using type = std::index_sequence; }; template using make_integer_sequence_from_to = typename make_integer_sequence_from_to_impl<_Tp, IFrom, ITo>::type; } #if __cplusplus < 201703L namespace std { //template>{}, int> = 0> //using enable_ template struct invoke_result_impl; template struct invoke_result_impl>{}>, Fn, Args...> { typedef decltype(std::mem_fn(std::declval())(std::declval()...)) type; }; template struct invoke_result_impl>{}>, Fn, Args...> { typedef decltype(std::declval()(std::declval()...)) type; }; template using invoke_result = invoke_result_impl; template using invoke_result_t = typename invoke_result::type; template>{}, int> = 0 > constexpr invoke_result_t invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward(args)...))) { return std::mem_fn(f)(std::forward(args)...); } template>{}, int> = 0> constexpr invoke_result_t invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...))) { return std::forward(f)(std::forward(args)...); } } namespace std { namespace detail { template struct apply_result_impl; // TODO: apply_result is not part of C++17, therefore we should move this out of // the #if block template struct apply_result_impl> { //typedef std::invoke_result_t...> type; typedef std::invoke_result_t(std::declval()))...> type; }; template using apply_result = apply_result_impl>::value>>; template using apply_result_t = typename apply_result::type; template constexpr apply_result_t apply_impl( F&& f, Tuple&& t, std::index_sequence ) { return std::invoke(std::forward(f), std::get(std::forward(t))...); } } // namespace detail template constexpr detail::apply_result_t apply(F&& f, Tuple&& t) { return detail::apply_impl(std::forward(f), std::forward(t), std::make_index_sequence>::value>{}); } } namespace std { template struct identity { using type = T; }; template struct overload_resolver; template<> struct overload_resolver<> { void operator()() const; }; template struct overload_resolver : overload_resolver { using overload_resolver::operator(); identity operator()(T) const; }; template struct index_of : integral_constant::value + 1)> {}; template struct index_of : integral_constant {}; /** * @brief Heavily simplified version of the C++17 std::variant. * Whatever compiles should work as one would expect from the C++17 variant. */ template class variant; // Empty variant is ill-formed. Only used for clean recursion here. template<> class variant<> { public: using storage_t = char[0]; storage_t content_; static void selective_destructor(char* storage, size_t index) { throw; } static void selective_copy_constuctor(char* target, const char* source, size_t index) { throw; } static bool selective_eq(const char* lhs, const char* rhs, size_t index) { throw; } static bool selective_neq(const char* lhs, const char* rhs, size_t index) { throw; } template static void selective_invoke_const(const char* content, size_t index, TFunc functor, TArgs&&... args) { throw; } template static void selective_invoke(const char* content, size_t index, TFunc functor, TArgs&&... args) { throw; } }; template class variant { public: using storage_t = char[sizeof(T) > sizeof(typename variant::storage_t) ? sizeof(T) : sizeof(typename variant::storage_t)]; static void selective_copy_constuctor(char* target, const char* source, size_t index) { if (index == 0) { new ((T*)target) T{*(T*)source}; // in-place construction using first type's copy constructor } else { variant::selective_copy_constuctor(target, source, index - 1); } } static void selective_destructor(char* storage, size_t index) { if (index == 0) { ((T*)storage)->~T(); } else { variant::selective_destructor(storage, index - 1); } } static bool selective_eq(const char* lhs, const char* rhs, size_t index) { if (index == 0) { return ((*(T*)lhs) == (*(T*)rhs)); } else { return variant::selective_eq(lhs, rhs, index - 1); } } static bool selective_neq(const char* lhs, const char* rhs, size_t index) { if (index == 0) { return ((*(T*)lhs) != (*(T*)rhs)); } else { return variant::selective_neq(lhs, rhs, index - 1); } } template static void selective_invoke_const(const char* content, size_t index, TFunc functor, TArgs&&... args) { if (index == 0) { functor(*(T*)content, std::forward(args)...); } else { variant::selective_invoke_const(content, index - 1, functor, std::forward(args)...); } } template static void selective_invoke(char* content, size_t index, TFunc functor, TArgs&&... args) { if (index == 0) { functor(*(T*)content, std::forward(args)...); } else { variant::selective_invoke(content, index - 1, functor, std::forward(args)...); } } variant() : index_(0) { new ((T*)content_) T{}; // in-place construction using first type's default constructor } variant(const variant & other) : index_(other.index_) { selective_copy_constuctor(content_, other.content_, index_); } variant(variant&& other) : index_(other.index_) { // TODO: implement selective_copy_constuctor(content_, other.content_, index_); } // Find the best match out of `T, Ts...` with `TArg` as the argument. template using best_match = decltype(overload_resolver()(std::declval())); template::type> //, typename=typename std::enable_if_t, variant>::value)>, typename TTarget=decltype(indicator_func(std::forward(std::declval()))), typename TIndex=index_of> variant(TArg&& arg) { new ((TTarget*)content_) TTarget{std::forward(arg)}; index_ = index_of::value; } ~variant() { selective_destructor(content_, index_); } inline variant& operator=(const variant & other) { selective_destructor(content_, index_); index_ = other.index_; selective_copy_constuctor(content_, other.content_, index_); return *this; } inline bool operator==(const variant& rhs) const { return (index_ == rhs.index_) && selective_eq(this->content_, rhs.content_, index_); } inline bool operator!=(const variant& rhs) const { return (index_ != rhs.index_) || selective_neq(this->content_, rhs.content_, index_); } template void invoke(TFunc functor, TArgs&&... args) const { selective_invoke_const(content_, index_, functor, std::forward(args)...); } template void invoke(TFunc functor, TArgs&&... args) { selective_invoke(content_, index_, functor, std::forward(args)...); } storage_t content_; size_t index_; size_t index() const { return index_; } }; template std::tuple_element_t>& get(std::variant& val) { if (val.index() != I) throw; using T = std::tuple_element_t>; return *((T*)val.content_); } template T& get(std::variant& val) { constexpr size_t index = std::index_of::value; return std::get(val); } /// Tag type to disengage optional objects. struct nullopt_t { // Do not user-declare default constructor at all for // optional_value = {} syntax to work. // nullopt_t() = delete; // Used for constructing nullopt. enum class _Construct { _Token }; // Must be constexpr for nullopt_t to be literal. explicit constexpr nullopt_t(_Construct) { } }; constexpr nullopt_t nullopt { nullopt_t::_Construct::_Token }; template class optional { public: using storage_t = char[sizeof(T)]; optional() : has_value_(false) {} optional(nullopt_t val) : has_value_(false) {} optional(const optional & other) : has_value_(other.has_value_) { if (has_value_) new ((T*)content_) T{*(T*)other.content_}; } optional(optional&& other) : has_value_(other.has_value_) { if (has_value_) new ((T*)content_) T{*(T*)other.content_}; } optional(T& arg) { new ((T*)content_) T{arg}; has_value_ = true; } optional(T&& arg) { new ((T*)content_) T{std::forward(arg)}; has_value_ = true; } ~optional() { if (has_value_) ((T*)content_)->~T(); } inline optional& operator=(const optional & other) { (**this).~T(); new (this) optional{other}; return *this; } inline bool operator==(const optional& rhs) const { return (!has_value_ && !rhs.has_value_) || (*(T*)content_ == *(T*)rhs.content_); } inline bool operator!=(const optional& rhs) const { return !(*this == rhs); } inline T& operator*() { return *(T*)content_; } inline T* operator->() { return (T*)content_; } storage_t content_; size_t has_value_; size_t has_value() const { return has_value_; } }; template optional make_optional(T&& val) { return optional{std::forward(val)}; } template optional make_optional(T& val) { return optional{val}; } } // namespace std #else #include #include #endif /* Stuff that should be in the STL but isn't ---------------------------------*/ // source: https://en.cppreference.com/w/cpp/experimental/to_array namespace detail { template constexpr std::array, N> to_array_impl(T (&a)[N], std::index_sequence) { return { {a[I]...} }; } template constexpr std::array, N> to_array(T (&a)[N]) { return detail::to_array_impl(a, std::make_index_sequence{}); } } /* Custom utils --------------------------------------------------------------*/ // @brief Supports various queries on a list of types template class TypeChecker; template class TypeChecker { public: using DecayedT = typename std::decay::type; // @brief Returns false if type T is equal to U or inherits from U. Returns true otherwise. template constexpr static inline bool first_is_not() { return !std::is_same::value && !std::is_base_of::value; } // @brief Returns true if all types [T, Ts...] are either equal to U or inherit from U. template constexpr static inline bool all_are() { return std::is_base_of::value && TypeChecker::template all_are(); } constexpr static const size_t count = TypeChecker::count + 1; }; template<> class TypeChecker<> { public: template constexpr static inline bool first_is_not() { return std::true_type::value; } template constexpr static inline bool all_are() { return std::true_type::value; } constexpr static const size_t count = 0; }; template TypeChecker make_type_checker(Ts ...) { return TypeChecker(); } #include #define ENABLE_IF(...) \ typename = typename std::enable_if_t<__VA_ARGS__> #define ENABLE_IF_SAME(a, b, type) \ template typename std::enable_if_t::value, type> template M get_member_type(M T:: *); #define GET_TYPE_OF(mem) decltype(get_member_type(mem)) //#include // @brief Statically asserts that T is derived from type BaseType #define EXPECT_TYPE(T, BaseType) static_assert(std::is_base_of::type>::value || std::is_convertible::type, BaseType>::value, "expected template argument of type " #BaseType) //#define EXPECT_TYPE(T, BaseType) static_assert(, "expected template argument of type " #BaseType) template class function_traits { public: template static TRet invoke(TObj& obj, TRet(TObj::*func_ptr)(TArgs...), std::tuple packed_args, TUnpackedArgs ... args) { return invoke(obj, func_ptr, packed_args, std::forward(args)..., std::get(packed_args)); } template static TRet invoke(TObj& obj, TRet(TObj::*func_ptr)(TArgs...), std::tuple packed_args, TArgs ... args) { return (obj.*func_ptr)(std::forward(args)...); } }; /* @brief return_type::type represents the C++ native return type * of a function returning 0 or more arguments. * * For an empty TypeList, the return type is void. For a list with * one type, the return type is equal to that type. For a list with * more than one items, the return type is a tuple. */ template struct return_type; template<> struct return_type<> { typedef void type; }; template struct return_type { typedef T type; }; template struct return_type { typedef std::tuple type; }; template struct static_function_traits; // TODO: All invoke-related functions should be superseeded by a proper std::apply implementation #if 0 template struct static_function_traits, std::tuple> { using TRet = typename return_type::type; //template //static std::tuple invoke(std::tuple packed_args, TUnpackedInputs ... args) { // return invoke(packed_args, args..., std::get(packed_args)); //} template static std::tuple invoke(std::tuple& packed_args) { return invoke_impl(packed_args, std::make_index_sequence()); } template static std::tuple invoke_impl(std::tuple packed_args, std::index_sequence) { return invoke_impl_2(std::get(packed_args)...); } //template //static std::enable_if_t<(sizeof...(TOutputs) == 0), std::tuple> template>*/> static std::enable_if_t<(IOutputs == 0), std::tuple> invoke_impl_2(TInputs ... args) { Function(args...); return std::make_tuple<>(); } //template //static std::enable_if_t<(sizeof...(TOutputs) == 1), std::tuple> template>*/> static std::enable_if_t<(IOutputs == 1), std::tuple> invoke_impl_2(TInputs ... args) { return std::make_tuple(Function(args...)); } // // template= 2)> // static /* std::enable_if_t= 2, */ std::tuple //> // invoke_impl_2(std::tuple packed_args, TInputs ... args) { // return Function(args...); // } }; /* @brief Invoke a class member function with a variable number of arguments that are supplied as a tuple Example usage: class MyClass { public: int MyFunction(int a, int b) { return 0; } }; MyClass my_object; std::tuple my_args(3, 4); // arguments are supplied as a tuple int result = invoke_function_with_tuple(my_object, &MyClass::MyFunction, my_args); */ template TRet invoke_function_with_tuple(TObj& obj, TRet(TObj::*func_ptr)(TArgs...), std::tuple packed_args) { return function_traits::template invoke<0>(obj, func_ptr, packed_args); } template(*Function)(TIn...)> std::tuple invoke_with_tuples(std::tuple inputs) { static_function_traits::template invoke<0>(inputs); } #endif template struct sum_impl; template struct sum_impl { static constexpr TInt value = 0; }; template struct sum_impl { static constexpr TInt value = I + sum_impl::value; }; template using sum = sum_impl; // source: https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ #if defined NDEBUG # define X_ASSERT(CHECK) void(0) #else # define X_ASSERT(CHECK) \ ( (CHECK) ? void(0) : []{assert(!#CHECK);}() ) #endif template struct for_each_in_tuple_result_impl; template struct for_each_in_tuple_result_impl> { typedef std::tuple(std::declval())(std::get(std::declval())))...> type; }; template using for_each_in_tuple_result = for_each_in_tuple_result_impl>::value>>; template using for_each_in_tuple_result_t = typename for_each_in_tuple_result::type; template for_each_in_tuple_result_t for_each_in_tuple_impl(Fn&& f, Tuple&& t, std::index_sequence) { return for_each_in_tuple_result_t(std::forward(f)(std::get(t))...); } template for_each_in_tuple_result_t for_each_in_tuple(Fn&& f, Tuple&& t) { return for_each_in_tuple_impl(std::forward(f), std::forward(t), std::make_index_sequence>::value>{}); } //template //for_each_in_tuple_result_t for_each_in_tuple(Fn&& f, Tuple&& t) { // return 5; //} /* constexpr strings --------------------------------------------------------*/ /* adapted from: * https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/ */ // TODO: the functionality // sstring::substring, sstring::get_last_part and sstring::after_last_index_of and sstring::last_index_of // was removed during refactoring. Add again if needed. /** * @brief Represents a string that is known at compile time by encoding it as a * type. */ template struct sstring { static constexpr const char chars[] = {CHARS..., 0}; static constexpr const char* c_str() { return chars; } static constexpr size_t size() { return sizeof...(CHARS); } static constexpr std::array as_array() { return {CHARS...}; } template constexpr bool operator==(const sstring & other) const { return as_array() == other.as_array(); } }; template constexpr const char sstring::chars[/*sizeof...(CHARS) + 1*/]; template struct sstring_concat_impl; template struct sstring_concat_impl, sstring> { using type = sstring; }; /** @brief Represents the result type of concatenating two static strings */ template using sstring_concat_t = typename sstring_concat_impl::type; /** @brief Concatenates two static strings */ template constexpr sstring operator+(sstring, sstring) { return {}; } /** @brief Helper class for the MAKE_SSTRING macro */ template struct sstring_builder; template struct sstring_builder<0, CHAR, CHARS...> { using type = sstring<>; }; template struct sstring_builder { using type = sstring_concat_t, typename sstring_builder::type>; }; template using sstring_builder_t = typename sstring_builder::type; #define MACRO_GET_1(str, i) \ (sizeof(str) > (i) ? str[(i)] : 0) #define MACRO_GET_4(str, i) \ MACRO_GET_1(str, i+0), \ MACRO_GET_1(str, i+1), \ MACRO_GET_1(str, i+2), \ MACRO_GET_1(str, i+3) #define MACRO_GET_16(str, i) \ MACRO_GET_4(str, i+0), \ MACRO_GET_4(str, i+4), \ MACRO_GET_4(str, i+8), \ MACRO_GET_4(str, i+12) #define MACRO_GET_64(str, i) \ MACRO_GET_16(str, i+0), \ MACRO_GET_16(str, i+16), \ MACRO_GET_16(str, i+32), \ MACRO_GET_16(str, i+48) /** * @brief Builds a compile-time string type from a string literal. * * Passing more than 64 characters will prune the string. * * Usage: * MAKE_SSTRING("hello world") my_str{}; * or * auto my_str = MAKE_SSTRING("hello world"){}; * * Both examples create a compile-time variable "my_str" of which the type * itself stores the content "hello world". */ #define MAKE_SSTRING(literal) sstring_builder_t /*namespace std { template static std::ostream& operator<<(std::ostream& stream, const sstring& val) { stream << val.chars; return stream; } }*/ template struct join_sstring_impl; template struct join_sstring_impl> { using type = sstring<>; }; template struct join_sstring_impl, sstring> { using type = sstring; }; template struct join_sstring_impl, sstring, TStr...> { using type = sstring_concat_t, typename join_sstring_impl, TStr...>::type>; }; template using join_sstring_t = typename join_sstring_impl::type; template constexpr join_sstring_t join_sstring(const TDelimiter& delimiter, const TStr& ... str) { return {}; } template using sstring_arr = std::tuple...>; // source: https://stackoverflow.com/questions/40159732/return-other-value-if-key-not-found-in-the-map template TValue& get_or(std::unordered_map& m, const TKey& key, TValue& default_value) { auto it = m.find(key); if (it == m.end()) { return default_value; } else { return it->second; } } template TValue* get_ptr(std::unordered_map& m, const TKey& key) { auto it = m.find(key); if (it == m.end()) return nullptr; else return &(it->second); } template std::true_type is_complete_impl(T *); std::false_type is_complete_impl(...); /** @brief is_complete resolves to std::true_type if T is complete * and to std::false_type otherwise. This can be used to check if a certain template * specialization exists. **/ template using is_complete = decltype(is_complete_impl(std::declval())); template struct dynamic_get_impl { template static TRet* get(size_t i, TTuple& t) { if (i == I::value) return &static_cast(std::get(t)); else if (i > I::value) return dynamic_get_impl, TRet, Ts...>::get(i, t); return nullptr; // this should not happen } }; template struct dynamic_get_impl, TRet, Ts...> { static TRet* get(size_t i, const std::tuple& t) { return nullptr; } }; template TRet* dynamic_get(size_t i, std::tuple& t) { return dynamic_get_impl, TRet, Ts...>::get(i, t); } template TRet* dynamic_get(size_t i, const std::tuple& t) { return dynamic_get_impl, TRet, Ts...>::get(i, t); } template class simple_iterator : std::iterator { TDereferenceable *container_; size_t i_; public: using reference = TResult; explicit simple_iterator(TDereferenceable& container, size_t pos) : container_(&container), i_(pos) {} simple_iterator& operator++() { ++i_; return *this; } simple_iterator operator++(int) { simple_iterator retval = *this; ++(*this); return retval; } bool operator==(simple_iterator other) const { return (container_ == other.container_) && (i_ == other.i_); } bool operator!=(simple_iterator other) const { return !(*this == other); } bool operator<(simple_iterator other) const { return i_ < other.i_; } bool operator>(simple_iterator other) const { return i_ > other.i_; } bool operator<=(simple_iterator other) const { return (*this < other) || (*this == other); } bool operator>=(simple_iterator other) const { return (*this > other) || (*this == other); } TResult operator*() const { return (*container_)[i_]; } }; /** * @brief Extracts the argument types of a function signature and provides them * as a std::tuple. * TODO: if an STL alternative exists, use that */ template struct args_of; template struct args_of { using type = std::tuple; }; //template //struct args_of<_Mem_fn> { // using type = std::tuple; //}; template struct args_of { using type = std::tuple; }; template struct args_of : public args_of {}; template using args_of_t = typename args_of::type; /** * @brief Extracts the return type of a function signature * * This is provided because std::result_of is deprecated since C++17 */ template struct result_of; template struct result_of { using type = TRet; }; template struct result_of { using type = TRet; }; template struct result_of { using type = TRet; }; template using result_of_t = typename result_of::type; /** * @brief Returns the type that results when concatenating multiple tuples */ template using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); template constexpr std::array array_cat_impl(std::array arr1, std::array arr2, std::index_sequence, std::index_sequence) { return { arr1[PACK1]..., arr2[PACK2]... }; } template constexpr std::array array_cat(std::array arr1, std::array arr2) { return array_cat_impl(arr1, arr2, std::make_index_sequence(), std::make_index_sequence()); } /** * @brief Returns the type that results when concatenating multiple tuples */ template using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); /** * @brief Ensures that a given type is wrapped in a tuple */ template struct as_tuple { using type = std::tuple; }; template<> struct as_tuple { using type = std::tuple<>; }; template struct as_tuple> { using type = std::tuple; }; template using as_tuple_t = typename as_tuple::type; /** * @brief Removes a reference OR pointer from the given type. * * This is similar to std::remove_reference, however it can also remove a * pointer and it does not work for types that are neither a reference or * a pointer. */ template struct remove_ref_or_ptr { static_assert(std::is_reference() || std::is_pointer(), "the type T is neither a reference or a pointer"); }; template struct remove_ref_or_ptr { using type = T; }; template struct remove_ref_or_ptr { using type = T; }; template using remove_ref_or_ptr_t = typename remove_ref_or_ptr::type; /** * @brief Applies remove_ref_or_ptr_t to every type of a tuple type */ template struct remove_refs_or_ptrs_from_tuple; template struct remove_refs_or_ptrs_from_tuple> { using type = std::tuple...>; }; template using remove_refs_or_ptrs_from_tuple_t = typename remove_refs_or_ptrs_from_tuple::type; /** * @brief The convert(val) function returns a reference or a pointer to val * depending on TTo. * TODO: this could be a functor */ template struct add_ref_or_ptr; template struct add_ref_or_ptr { static T& convert(T& value) { return value; } }; template struct add_ref_or_ptr { static T* convert(T& value) { return &value; } }; /** * @brief The convert() function turns a given tuple of values into a tuple of * pointers or references based on the template argument TTo. */ template struct add_ref_or_ptr_to_tuple; template struct add_ref_or_ptr_to_tuple> { template static std::tuple convert_impl(std::tuple&& t, std::index_sequence) { using to_type = std::tuple; to_type result(add_ref_or_ptr>::convert(std::get(t))...); return result; } template static std::tuple convert(std::tuple&& t) { static_assert(sizeof...(TFrom) == sizeof...(TTo), "both tuples must have the same size"); return convert_impl(std::forward>(t), std::make_index_sequence()); } }; template struct add_ptrs_to_tuple_type; template struct add_ptrs_to_tuple_type> { using type = std::tuple; }; template using add_ptrs_to_tuple_t = typename add_ptrs_to_tuple_type::type; template struct add_refs_to_tuple_type; template struct add_refs_to_tuple_type> { using type = std::tuple; }; template using add_refs_to_tuple_t = typename add_refs_to_tuple_type::type; template struct is_tuple: std::false_type {}; template struct is_tuple>: std::true_type {}; template struct tuple_select_type_impl; template struct tuple_select_type_impl, TTuple> { using type = std::tuple...>; }; template typename tuple_select_type_impl, TTuple>::type tuple_select_impl(TTuple tuple, std::index_sequence) { return typename tuple_select_type_impl, TTuple>::type(std::get(tuple)...); }; template struct tuple_take_type { static_assert(I <= std::tuple_size::value, "cannot take more elements than tuple size"); using type = typename tuple_select_type_impl, TTuple>::type; }; template using tuple_take_t = typename tuple_take_type::type; /** * @brief Returns the first I elements from the tuple as a tuple. * The resulting type is tuple_take_t. * See also: tuple_skip */ template tuple_take_t tuple_take(TTuple tuple) { return tuple_select_impl(tuple, std::make_index_sequence{}); }; template struct tuple_skip_type { static_assert(I <= std::tuple_size::value, "cannot skip more elements than tuple size"); using type = typename tuple_select_type_impl::value>, TTuple>::type; }; template using tuple_skip_t = typename tuple_skip_type::type; /** * @brief Returns all but the first I elements from the tuple as a tuple. * The resulting type is tuple_skip_t. * See also: tuple_take */ template tuple_skip_t tuple_skip(TTuple tuple) { return tuple_select_impl(tuple, fibre::make_integer_sequence_from_to::value>{}); }; template struct repeat_type_impl { using type = typename repeat_type_impl::type; }; template struct repeat_type_impl<0, T, Ts...> { using type = std::tuple; }; template using repeat_t = typename repeat_type_impl::type; #endif // __CPP_UTILS_HPP