// // Copyright (c) 2022-2024 Kris Jusiak (kris at jusiak dot net) // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifndef MP #define MP 1'0'0 // SemVer #if defined(__GNUC__) #pragma GCC system_header // -Wnon-template-friend #endif /** * ~~Template~~ Meta-Programming library (https://github.com/boost-ext/mp) */ namespace mp::inline v1_0_0 { using size_t = decltype(sizeof(int)); namespace utility { template T&& declval(); template struct integer_sequence { }; template using index_sequence = integer_sequence; template using make_index_sequence = #if defined(__clang__) || defined(_MSC_VER) __make_integer_seq; #else index_sequence<__integer_pack(Size)...>; #endif } // namespace utility /** * Meta type object representation */ struct meta_info { }; #if defined(MP_META_INFO) using meta_t = const MP_META_INFO*; #elif (__cpp_constexpr >= 202306L) // [C++23] Constexpr cast from void* using meta_t = const void*; #else using meta_t = const meta_info*; #endif namespace detail { template struct meta_id { constexpr auto friend get(meta_id); }; template struct meta_info { using value_type = T; #if defined(MP_META_INFO) static constexpr MP_META_INFO info{}; #else static constexpr mp::meta_info info{}; #endif constexpr auto friend get(meta_id) { return meta_info{}; } }; } // namespace detail /** * Creates meta type * * @code * static_assert(meta == meta); * static_assert(meta != meta); * @endcode */ template inline constexpr meta_t meta = meta_t{&detail::meta_info::info}; /** * Returns underlying meta_info from meta type * * @code * static_assert(typeid(meta_info_of>) == typeid(meta_info)); * @endcode */ template using meta_info_of = decltype(get(detail::meta_id{})); /** * Returns underlying type from meta type * * @code * static_assert(typeid(type_of>) == typeid(void)); * @endcode */ template using type_of = typename meta_info_of::value_type; /** * Returns value of meta type * * @code * static_assert(42 = value_of_v>>); * @endcode */ template constexpr auto value_of_v = type_of::value; /** * Returns value of meta type underlying object */ template [[nodiscard]] constexpr decltype(auto) value_of(T&& t) { return meta_info_of::value_of(static_cast(t)); } /** * Minimal (not standard compliant) array * implementation optimized for fast compilation-times with meta_t * * @code * array v{meta, meta}; * assert(2 == v.size()); * assert(meta == v[0]); * assert(meta == v[1]); * @endcode */ template struct array { using value_type = T; [[nodiscard]] constexpr auto begin() const { return &data[0]; } [[nodiscard]] constexpr auto begin() { return &data[0]; } [[nodiscard]] constexpr auto end() const { return &data[0]+Size; } [[nodiscard]] constexpr auto end() { return &data[0]+Size; } [[nodiscard]] constexpr auto operator[](size_t I) const { return data[I]; } [[nodiscard]] constexpr auto& operator[](size_t I) { return data[I]; } [[nodiscard]] constexpr auto size() const { return Size; } [[nodiscard]] constexpr bool operator==(const array& other) const { for (auto i = 0u; i < Size; ++i) { if (data[i] != other.data[i]) { return false; } } return true; } [[nodiscard]] constexpr bool operator!=(const array& other) const { return !operator==(other); } T data[Size]; }; template array(T, Ts...) -> array; /** * Minimal (not standard compliant) inplace/static vector * implementation optimized for fast compilation-times with meta_t * * @code * vector v{meta, meta}; * assert(2 == v.size()); * assert(meta == v[0]); * assert(meta == v[1]); * @endcode */ template struct vector { using value_type = T; constexpr vector() = default; template constexpr explicit vector(Ts&&... ts) : data{ts...}, size_{sizeof...(ts)} { } template().begin(), utility::declval().end())> constexpr vector(TRange range) { for (const T& t : range) { data[size_++] = t; } } constexpr void push_back(const T& t) { data[size_++] = t; } constexpr void emplace_back(T&& t) { data[size_++] = static_cast(t); } [[nodiscard]] constexpr auto begin() const { return &data[0]; } [[nodiscard]] constexpr auto begin() { return &data[0]; } [[nodiscard]] constexpr auto end() const { return &data[0]+size_; } [[nodiscard]] constexpr auto end() { return &data[0]+size_; } [[nodiscard]] constexpr auto operator[](size_t i) const { return data[i]; } [[nodiscard]] constexpr auto& operator[](size_t i) { return data[i]; } [[nodiscard]] constexpr auto size() const { return size_; } [[nodiscard]] constexpr auto& size() { return size_; } [[nodiscard]] constexpr auto capacity() const { return Size; } template [[nodiscard]] constexpr auto operator==(const vector& other) const { if (size_ != other.size_) { return false; } for (auto i = 0u; i < size_; ++i) { if (data[i] != other.data[i]) { return false; } } return true; } template [[nodiscard]] constexpr auto operator!=(const vector& other) const { return !operator==(other); } constexpr void clear() { size_ = {}; } union { T data[Size]{}; }; // !trivial union size_t size_{}; }; template vector(T, Ts...) -> vector; /** * Applies expression expr to `R...>` * * @code * static_assert(typeid(variant) == typeid(apply([] { return vector{meta}; }))); * @endcode */ template class R, class Expr> [[nodiscard]] constexpr auto apply(Expr expr) { constexpr auto v = expr(); return [v](utility::index_sequence) { return R...>{}; }(utility::make_index_sequence{}); } /** * Applies vector V to `R...>` * * @code * static_assert(typeid(variant) == typeid(apply}>)); * @endcode */ #if defined(__cpp_nontype_template_args) template class R, auto V> inline constexpr auto apply_v = [](utility::index_sequence) { return R...>{}; }(utility::make_index_sequence{}); #endif /** * Applies vector V with object t to `R{value_of(t)...} */ #if defined(__cpp_nontype_template_args) template class R, auto V, class T> [[nodiscard]] constexpr auto apply(T&& t) { return [&](utility::index_sequence) { return R{meta_info_of::value_of(static_cast(t))...}; }(utility::make_index_sequence{}); } #endif /** * Applies vector V with object t to `R...> */ #if defined(__cpp_nontype_template_args) template class R, auto V> [[nodiscard]] constexpr auto apply() { return [](utility::index_sequence) { return R...>{}; }(utility::make_index_sequence{}); } #endif /** * Alternative to write `decltype(apply_v))` * * @code * static_assert(typeid(variant) == typeid(apply_t}; }>)); * @endcode */ #if defined(__cpp_nontype_template_args) #if defined(__clang__) || defined(_MSC_VER) template class R, auto V> using apply_t = decltype(apply()); #else namespace detail { template class R, auto V, size_t... Ns> using apply_t = R...>; } // namespace detail template class R, auto V> using apply_t = detail::apply_t; #endif #endif /** * Iterates over all elements of constexpr container * * @code * constexpr vector v{meta}; * for_each([] { * static_assert(typeid(int) == typeid(type_of)); * }); * @endcode */ #if (__cpp_generic_lambdas >= 201707L) template constexpr void for_each(Fn fn) { [fn](utility::index_sequence) { (fn.template operator()(), ...); }(utility::make_index_sequence{}); } #endif } // namespace mp #if !defined(DISABLE_STATIC_ASSERT_TESTS) namespace mp::inline v1_0_0::test { template struct type_list { }; template struct _i { static constexpr auto value = I; }; template inline constexpr auto is_same_v = false; template inline constexpr auto is_same_v = true; void failed(); constexpr void expect(bool cond) { if (!cond) { failed(); } } } // namespace mp::test static_assert(([] { using mp::test::expect; // mp::test { static_assert(!mp::test::is_same_v); static_assert(!mp::test::is_same_v); static_assert(!mp::test::is_same_v); static_assert(!mp::test::is_same_v); static_assert(mp::test::is_same_v); static_assert(mp::test::is_same_v); } // mp::utilty::declval { struct s { s() = delete; }; static_assert(sizeof(s) == sizeof(mp::utility::declval())); static_assert(sizeof(s&) == sizeof(mp::utility::declval())); } // mp::utility::index_sequence { static_assert([](mp::utility::integer_sequence){ return true; }(mp::utility::make_index_sequence<0>{})); static_assert([](mp::utility::integer_sequence){ return true; }(mp::utility::make_index_sequence<1>{})); static_assert([](mp::utility::integer_sequence){ return true; }(mp::utility::make_index_sequence<2>{})); static_assert([](mp::utility::index_sequence<>){ return true; }(mp::utility::make_index_sequence<0>{})); static_assert([](mp::utility::index_sequence<0>){ return true; }(mp::utility::make_index_sequence<1>{})); static_assert([](mp::utility::index_sequence<0, 1>){ return true; }(mp::utility::make_index_sequence<2>{})); } // mp::meta { static_assert(mp::meta == mp::meta); static_assert(mp::meta != mp::meta); static_assert(mp::meta == mp::meta); static_assert(mp::meta == mp::meta); static_assert(mp::meta == mp::meta); } // mp::type_of { enum class e : int { }; static_assert(mp::test::is_same_v>>); static_assert(mp::test::is_same_v>>); static_assert(mp::test::is_same_v>>); static_assert(mp::test::is_same_v>>); constexpr mp::vector v{mp::meta, mp::meta}; static_assert(mp::test::is_same_v>); static_assert(mp::test::is_same_v>); } // mp::value_of { static_assert(0 == mp::value_of_v>>); static_assert('a' == mp::value_of_v>>); static_assert(42u == mp::value_of_v>>); } // mp::apply { static_assert([](mp::test::type_list<>){ return true; }(mp::apply([] { return mp::vector{}; }))); static_assert([](mp::test::type_list){ return true; }(mp::apply([] { return mp::vector{mp::meta}; }))); static_assert([](mp::test::type_list){ return true; }(mp::apply([] { return mp::vector{mp::meta, mp::meta}; }))); } #if defined(__cpp_generic_lambdas) && (__cpp_generic_lambdas >= 201707L) // mp::for_each { { constexpr mp::vector m{}; mp::vector v{}; mp::for_each([&] { v.push_back(meta); }); expect(0 == v.size()); } { mp::for_each}>([&] { expect(meta == mp::meta); }); } #if !defined(_MSC_VER) { constexpr mp::vector m{mp::meta, mp::meta}; mp::vector v{}; mp::for_each([&] { v.push_back(meta); }); expect(2 == v.size()); expect(m[0] == v[0]); expect(m[1] == v[1]); } #endif } #endif // mp::array { { mp::array a{}; expect(1 == a.size()); } { mp::array a{1, 2}; expect(2u == a.size()); expect(1 == a[0]); expect(2 == a[1]); } { expect(mp::array{1, 2} == mp::array{1, 2}); expect(mp::array{1, 2} != mp::array{1, 3}); } { mp::array a{1}; a[0] = 2; expect(2 == a[0]); } { mp::array a{1, 2, 3}; expect(3u == a.size()); expect(a.begin() != a.end()); expect(a.size() == mp::size_t(a.end() - a.begin())); expect(a.end() == a.begin() + a.size()); } } // mp::vector { { mp::vector v{}; expect(0u == v.size()); expect(1 == v.capacity()); } { mp::vector v{1, 2, 3}; expect(3u == v.size()); expect(3 == v.capacity()); } { constexpr mp::vector v1{1}; static_assert(1u == v1.size()); static_assert(1 == v1[0]); constexpr mp::vector v2{v1}; static_assert(1u == v2.size()); static_assert(1 == v2[0]); } { mp::vector v1{1, 2}; expect(2u == v1.size()); expect(4 == v1.capacity()); expect(1 == v1[0]); expect(2 == v1[1]); mp::vector v2{v1}; expect(2u == v2.size()); expect(2 == v2.capacity()); expect(1 == v2[0]); expect(2 == v2[1]); } { mp::vector v; expect(0u == v.size()); expect(2 == v.capacity()); v.push_back(1); expect(1u == v.size()); expect(1 == v[0]); v.push_back(2); expect(2u == v.size()); expect(2 == v[1]); } { mp::vector v{42}; expect(42 == v[0]); v[0]++; expect(43 == v[0]); v[0] = 0; expect(0 == v[0]); } { mp::vector v{1, 2}; expect(2u == v.size()); expect(2 == v.capacity()); v.clear(); expect(0u == v.size()); expect(2 == v.capacity()); } { mp::vector v{}; expect(0u == v.size() && 0u == mp::size_t(v.end() - v.begin())); expect(v.begin() == v.end()); v.emplace_back(1); expect(1u == v.size() && 1 == mp::size_t(v.end() - v.begin())); expect(v.begin() != v.end()); v.emplace_back(2); expect(2u == v.size() && 2u == mp::size_t(v.end() - v.begin())); expect(v.begin() != v.end()); } { expect(mp::vector{1} == mp::vector{1}); expect(mp::vector{1} == mp::vector{1}); expect(mp::vector{1} != mp::vector{2}); expect(mp::vector{1} != mp::vector{2}); } } }(), true)); #endif #endif // MP