| // Copyright (C) 2020 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| #pragma once |
| #include "dctv.h" |
| |
| #include <boost/fusion/adapted.hpp> |
| #include <boost/fusion/container.hpp> |
| #include <boost/fusion/tuple.hpp> |
| #include <boost/hana.hpp> |
| #include <boost/hana/ext/boost/fusion.hpp> |
| #include <boost/hana/ext/std/array.hpp> |
| #include <boost/hana/ext/std/tuple.hpp> |
| #include <boost/preprocessor.hpp> |
| #include <boost/version.hpp> |
| |
| #if BOOST_VERSION < 106700 |
| # error "DCTV requires Boost 1.67 or newer" |
| #endif |
| |
| namespace dctv { |
| |
| namespace hana = boost::hana; |
| namespace fusion = boost::fusion; |
| |
| // TODO(dancol): look into using hana's experimental types container. |
| |
| template <typename Struct> |
| constexpr auto struct_field_names = decltype( |
| hana::unpack(std::declval<Struct>(), |
| hana::on(hana::make_tuple, |
| hana::first))){}; |
| |
| template<typename Struct> |
| constexpr auto struct_field_types = decltype( |
| hana::unpack(std::declval<Struct>(), |
| hana::on( |
| hana::make_tuple, |
| hana::compose(hana::typeid_, |
| hana::second)))){}; |
| |
| // N.B. the hana::to_tuple here is not redundant: it forces eager |
| // evaluation of the otherwise lazily-evaluated fusion vector. "But |
| // we're referentially transparent, so that shouldn't matter!", you |
| // might object. Ah, but we're only _mostly_ referentially |
| // transparent: Clang at least objects with various incomplete type |
| // errors if we try to use the lazy vector directly to build |
| // x3 parsers. |
| template<typename Struct> |
| constexpr auto struct_field_types_from_fusion = decltype( |
| hana::transform( |
| hana::to_tuple(fusion::as_vector(std::declval<Struct>())), |
| hana::typeid_)){}; |
| |
| // CONSTEXPR_VALUE creates a "constexpr barrier" allowing us to |
| // static_assert on a function parameter provided the parameter's |
| // entire value is encoded in its type. hana::value() is supposed to |
| // constexpr-ify values, but there are still some situations where |
| // clang complains with hana::value but succeeds with CONSTEXPR_VALUE. |
| |
| template<typename T> struct HanaConstExprHack; |
| template<typename T, T c_value> |
| struct HanaConstExprHack<const hana::integral_constant<T, c_value>> { |
| static constexpr bool value = c_value; |
| }; |
| template<typename T, T c_value> |
| struct HanaConstExprHack<hana::integral_constant<T, c_value>> { |
| static constexpr T value = c_value; |
| }; |
| #define CONSTEXPR_VALUE(expr) \ |
| HanaConstExprHack<std::decay_t<decltype(expr)>>::value |
| |
| static_assert(CONSTEXPR_VALUE(hana::true_c)); |
| static_assert(!CONSTEXPR_VALUE(hana::false_c)); |
| static_assert(CONSTEXPR_VALUE(hana::char_c<1>)); |
| static_assert(!CONSTEXPR_VALUE(hana::char_c<0>)); |
| static_assert(CONSTEXPR_VALUE(hana::int_c<7>) == 7); |
| |
| // This beautiful preprocessor hack is due to Boost.Fusion. |
| // It transforms a value (foo,bar)(qux,spam)(blarg,banana) into |
| // ((foo,bar))((qux,spam))((blarg,banana)). The double parentheses |
| // are necessary for the Boost.Preprocessor sequence stuff to work |
| // correctly when the elements contained in the sequence are tuples, |
| // as they are for us. |
| |
| #define DCTV_WRAP_SEQ(seq) \ |
| BOOST_PP_CAT(DCTV_SEQ_CREATOR_0 seq,_END) |
| |
| // Like hana::make, but works via aggregate initialization instead of |
| // an explicit constructor. It's a variable template for a function |
| // object, so it composes normally. |
| |
| template<typename Aggregate> |
| constexpr auto make_aggregate = [](auto&&... args) { |
| return Aggregate{std::forward<decltype(args)>(args)...}; |
| }; |
| |
| template<typename Container> |
| auto |
| maybe_compile_time_container_size(const Container& c) |
| { |
| namespace h = hana; |
| auto probe = [](const auto& cv) -> |
| decltype(h::size_c<decltype(cv){}.size()>) |
| { |
| return decltype(h::size_c<decltype(cv){}.size()>){}; |
| }; |
| return h::sfinae(probe)(c); |
| } |
| |
| // Define a Hana struct using Fusion-like Boost.Preproessor sequence |
| // syntax, i.e., (type1, name1)(type2, name2)(type3, name3)..., with |
| // no intervening commas. Why? Because native Hana syntax requires |
| // that the last entry lack a trailing comma, and a prohibition on |
| // trailing commas is evil. |
| #define DCTV_DEFINE_HANA_STRUCT(name, fields) \ |
| DCTV_DEFINE_HANA_STRUCT_1(name, DCTV_WRAP_SEQ(fields)); /*NOLINT*/ \ |
| static_assert(true, "force terminating semicolon") |
| |
| #define DCTV_DEFINE_TAG(op_name) \ |
| constexpr auto op_name = ::boost::hana::type_c<struct op_name> /* NOLINT */ |
| |
| } // namespace dctv |
| |
| #include "meta_util-inl.h" |