blob: 1acd81e447bd30a69b99b5b276c08ad49b378bb7 [file] [log] [blame]
// 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"