boost/parser/tuple.hpp
// Copyright (C) 2020 T. Zachary Laine // Copyright Louis Dionne 2013-2017 // // 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 BOOST_PARSER_TUPLE_HPP #define BOOST_PARSER_TUPLE_HPP #include <boost/parser/config.hpp> #include <boost/parser/detail/detection.hpp> #if BOOST_PARSER_USE_STD_TUPLE #include <tuple> #else // Silence very verbose warnings about std::is_pod/std::is_literal being // deprecated. #if defined(__GNUC__) || defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" # pragma GCC diagnostic ignored "-Wunused-value" #endif #include <boost/hana.hpp> #if defined(__GNUC__) || defined(__clang__) # pragma GCC diagnostic pop #endif #endif namespace boost { namespace parser { namespace detail { // to_int() and parse_llong() taken from boost/hana/bool.hpp. constexpr int to_int(char c) { int result = 0; if (c >= 'A' && c <= 'F') { result = static_cast<int>(c) - static_cast<int>('A') + 10; } else if (c >= 'a' && c <= 'f') { result = static_cast<int>(c) - static_cast<int>('a') + 10; } else { result = static_cast<int>(c) - static_cast<int>('0'); } return result; } template<std::size_t N> constexpr long long parse_llong(const char (&arr)[N]) { long long base = 10; std::size_t offset = 0; if constexpr (N > 2) { bool starts_with_zero = arr[0] == '0'; bool is_hex = starts_with_zero && arr[1] == 'x'; bool is_binary = starts_with_zero && arr[1] == 'b'; if (is_hex) { // 0xDEADBEEF (hexadecimal) base = 16; offset = 2; } else if (is_binary) { // 0b101011101 (binary) base = 2; offset = 2; } else if (starts_with_zero) { // 012345 (octal) base = 8; offset = 1; } } long long number = 0; long long multiplier = 1; for (std::size_t i = 0; i < N - offset; ++i) { char c = arr[N - 1 - i]; if (c != '\'') { // skip digit separators number += to_int(c) * multiplier; multiplier *= base; } } return number; } } /** The tuple template alias used within Boost.Parser. This will be `boost::hana::tuple` if `BOOST_PARSER_USE_HANA_TUPLE` is defined, and `std::tuple` otherwise. */ #if BOOST_PARSER_USE_STD_TUPLE template<typename... Args> using tuple = std::tuple<Args...>; #else template<typename... Args> using tuple = hana::tuple<Args...>; #endif /** A template alias that is `boost::hana::integral_constant<T, I>` if `BOOST_PARSER_USE_HANA_TUPLE` is defined, and `std::integral_constant<T, I>` otherwise. */ #if BOOST_PARSER_USE_STD_TUPLE template<typename T, T I> using integral_constant = std::integral_constant<T, I>; #else template<typename T, T I> using integral_constant = hana::integral_constant<T, I>; #endif /** An accessor that returns a reference to the `I`-th data member of an aggregate struct or `boost::parser::tuple`. */ template<typename T, typename U, U I> constexpr decltype(auto) get(T && x, integral_constant<U, I> i); /** A template alias that is `boost::hana::llong<I>` if `BOOST_PARSER_USE_HANA_TUPLE` is defined, and `std::integral_constant<long long, I>` otherwise. */ template<long long I> using llong = integral_constant<long long, I>; namespace literals { /** A literal that can be used to concisely name `parser::llong` integral constants. */ template<char... chars> constexpr auto operator"" _c() { constexpr long long n = detail::parse_llong<sizeof...(chars)>({chars...}); return llong<n>{}; } } namespace detail { /** A tuple accessor that returns a reference to the `I`-th element. */ template<typename T, T I, typename... Args> constexpr decltype(auto) tuple_get(tuple<Args...> const & t, integral_constant<T, I>) { #if BOOST_PARSER_USE_STD_TUPLE return std::get<I>(t); #else return hana::at_c<I>(t); #endif } /** A tuple accessor that returns a reference to the `I`-th element. */ template<typename T, T I, typename... Args> constexpr decltype(auto) tuple_get(tuple<Args...> & t, integral_constant<T, I>) { #if BOOST_PARSER_USE_STD_TUPLE return std::get<I>(t); #else return hana::at_c<I>(t); #endif } /** A tuple accessor that returns a reference to the `I`-th element. */ template<typename T, T I, typename... Args> constexpr decltype(auto) tuple_get(tuple<Args...> && t, integral_constant<T, I>) { #if BOOST_PARSER_USE_STD_TUPLE return std::move(std::get<I>(t)); #else return std::move(hana::at_c<I>(t)); #endif } template<int N> struct ce_int { constexpr static int value = N; }; struct whatever { int _; template<typename T> operator T() const && noexcept { #if defined(__GNUC__) && __GNUC__ < 13 // Yuck. std::remove_reference_t<T> * ptr = nullptr; ptr += 1; // warning mitigation return *ptr; #else return std::declval<T>(); #endif } }; template<typename T, int... Is> constexpr auto constructible_expr_impl(std::integer_sequence<int, Is...>) -> decltype(T{whatever{Is}...}, ce_int<1>{}); template<typename T, typename N> using constructible_expr = decltype(detail::constructible_expr_impl<T>( std::make_integer_sequence<int, N::value>())); template<typename T, int... Is> constexpr int struct_arity_impl(std::integer_sequence<int, Is...>) { return ( detected_or_t<ce_int<0>, constructible_expr, T, ce_int<Is>>:: value + ... + 0); } // This often mistakenly returns 1 when you give it a struct with // private/protected members, because of copy/move construction. // Fortunately, we don't care -- we never assign from tuples of size // 1. template<typename T> constexpr int struct_arity_v = detail::struct_arity_impl<T>(std::make_integer_sequence< int, BOOST_PARSER_MAX_AGGREGATE_SIZE>()) - 1; template<typename T> constexpr int tuple_size_ = -1; template<typename... Elems> constexpr int tuple_size_<tuple<Elems...>> = sizeof...(Elems); template<typename T, typename Tuple, int... Is> auto assign_tuple_to_aggregate( T & x, Tuple tup, std::integer_sequence<int, Is...>) -> decltype(x = T{parser::get(std::move(tup), llong<Is>{})...}); template<typename T, typename Tuple, int... Is> auto tuple_to_aggregate(Tuple && tup, std::integer_sequence<int, Is...>) -> decltype(T{std::move(parser::get(tup, llong<Is>{}))...}) { return T{std::move(parser::get(tup, llong<Is>{}))...}; } template<typename T, typename Tuple> using tuple_to_aggregate_expr = decltype(detail::assign_tuple_to_aggregate( std::declval<T &>(), std::declval<Tuple>(), std::make_integer_sequence<int, tuple_size_<Tuple>>())); template<typename Struct, typename Tuple> constexpr bool is_struct_assignable_v = struct_arity_v<Struct> == tuple_size_<Tuple> ? is_detected_v<tuple_to_aggregate_expr, Struct, Tuple> : false; template<int N> struct tie_aggregate_impl { template<typename T> static constexpr auto call(T & x) { static_assert( sizeof(T) && false, "It looks like you're trying to use a struct larger than " "the limit."); } }; template<typename T> constexpr auto tie_aggregate(T & x) { static_assert(!std::is_union_v<T>); return tie_aggregate_impl<struct_arity_v<T>>::call(x); } template<typename Tuple, typename Tie, int... Is> auto aggregate_to_tuple( Tuple & tup, Tie tie, std::integer_sequence<int, Is...>) -> decltype(( (parser::get(tup, llong<Is>{}) = std::move(parser::get(tie, llong<Is>{}))), ..., (void)0)) { return ( (parser::get(tup, llong<Is>{}) = std::move(parser::get(tie, llong<Is>{}))), ..., (void)0); } template<typename Tuple, typename T> using aggregate_to_tuple_expr = decltype(detail::aggregate_to_tuple( std::declval<Tuple &>(), detail::tie_aggregate(std::declval<T &>()), std::make_integer_sequence<int, tuple_size_<Tuple>>())); template<typename Tuple, typename Struct> constexpr bool is_tuple_assignable_impl() { if constexpr ( std::is_aggregate_v<Struct> && struct_arity_v<Struct> == tuple_size_<Tuple>) { return is_detected_v<aggregate_to_tuple_expr, Tuple, Struct>; } else { return false; } } template<typename Tuple, typename Struct> constexpr bool is_tuple_assignable_v = is_tuple_assignable_impl<Tuple, Struct>(); template<typename T> struct is_tuple : std::false_type {}; template<typename... T> struct is_tuple<tuple<T...>> : std::true_type {}; } template<typename T, typename U, U I> constexpr decltype(auto) get(T && x, integral_constant<U, I> i) { using just_t = std::decay_t<T>; if constexpr (detail::is_tuple<just_t>::value) { return detail::tuple_get((T &&) x, i); } else if constexpr (std::is_aggregate_v<just_t>) { auto tup = detail::tie_aggregate(x); return detail::tuple_get(tup, i); } else { static_assert( sizeof(T) != sizeof(T), "boost::parser::get() is only defined for boost::parser::tuple " "and aggregate structs."); } } }} #include <boost/parser/detail/aggr_to_tuple_generated.hpp> #endif