From 05ddacb192ea993346b349c09e0731d712f83936 Mon Sep 17 00:00:00 2001 From: Samuel Just Date: Wed, 10 Aug 2016 15:26:38 -0700 Subject: [PATCH] common/: add match() utilities for boost::variant C++ doesn't have a sum type with nice pattern matching syntax. Fortunately, someone on stack overflow fixed that. Signed-off-by: Samuel Just --- src/common/function_signature.h | 50 +++++++ src/common/inline_variant.h | 241 ++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 src/common/function_signature.h create mode 100644 src/common/inline_variant.h diff --git a/src/common/function_signature.h b/src/common/function_signature.h new file mode 100644 index 0000000000000..58b284ec85f51 --- /dev/null +++ b/src/common/function_signature.h @@ -0,0 +1,50 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Copied from: + * https://github.com/exclipy/inline_variant_visitor/blob/master/function_signature.hpp + * which apparently copied it from + * http://stackoverflow.com/questions/4771417/how-to-get-the-signature-of-a-c-bind-expression + */ + +#ifndef FUNCTION_SIGNATURE_H +#define FUNCTION_SIGNATURE_H + +#include +#include +#include +#include +#include +#include + +#include + +template +struct signature_of_member +{ + typedef typename boost::function_types::result_type::type result_type; + typedef typename boost::function_types::parameter_types::type parameter_types; + typedef typename boost::mpl::pop_front::type base; + typedef typename boost::mpl::push_front::type L; + typedef typename boost::function_types::function_type::type type; +}; + +template +struct signature_of_impl +{ + typedef typename boost::function_types::function_type::type type; +}; + +template +struct signature_of_impl +{ + typedef typename signature_of_member::type type; +}; + +template +struct signature_of +{ + typedef typename signature_of_impl::value>::type type; +}; + +#endif diff --git a/src/common/inline_variant.h b/src/common/inline_variant.h new file mode 100644 index 0000000000000..b8a6bdb9760c1 --- /dev/null +++ b/src/common/inline_variant.h @@ -0,0 +1,241 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Copied from: + * https://github.com/exclipy/inline_variant_visitor/blob/master/inline_variant.hpp + */ + +#ifndef INLINE_VARIANT_H +#define INLINE_VARIANT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "function_signature.h" + +namespace detail { + +// A metafunction class for getting the argument type from a unary function or functor type +struct function_arg_extractor +{ + // Function is either a function type like void(int const&), or a functor - eg. a class with void operator(int) + // Sets type to the argument type with the constness and referenceness stripped (eg. int) + template + struct apply + { + private: + typedef typename boost::remove_const< typename boost::remove_reference::type >::type bare_type; + typedef typename signature_of::type normalized_function_type; + typedef typename boost::function_types::function_arity::type arity; + typedef typename boost::function_types::parameter_types::type parameter_types; + typedef typename boost::function_types::result_type::type result_type; + + BOOST_STATIC_ASSERT_MSG((arity::value == 1), "make_visitor called with a non-unary function"); + + typedef typename boost::mpl::front::type parameter_type; + public: + typedef typename boost::remove_const< typename boost::remove_reference::type >::type type; + }; +}; + +// A private helper fusion metafunction to construct a fusion map of functions +struct pair_maker +{ + template + struct result; + + template + struct result + { + typedef typename function_arg_extractor::apply::type arg_type; + typedef boost::fusion::pair type; + }; + + template + typename result::type operator()(Function a) const + { + return boost::fusion::make_pair< typename result::arg_type >(a); + } +}; + +// A metafunction class that asserts the second argument is in Allowed, and returns void +template +struct check_in +{ + template + struct apply + { + private: + BOOST_STATIC_ASSERT_MSG((boost::mpl::contains::value), + "make_visitor called with spurious handler functions"); + public: + typedef void type; + }; +}; + +// A functor template suitable for passing into apply_visitor. The constructor accepts the list of handler functions, +// which are then exposed through a set of operator()s +template +struct generic_visitor : boost::static_visitor, boost::noncopyable +{ +private: + typedef generic_visitor type; + + // Compute the function_map type + typedef boost::mpl::vector function_types; + typedef typename boost::mpl::transform::type arg_types; + // Check that the argument types are unique + typedef typename boost::mpl::fold< + arg_types, + boost::mpl::set0<>, + boost::mpl::insert + >::type arg_types_set; + BOOST_STATIC_ASSERT_MSG((boost::mpl::size::value == boost::mpl::size::value), + "make_visitor called with non-unique argument types for handler functions"); + + // Check that there aren't any argument types not in the variant types + typedef typename boost::mpl::fold >::type dummy; + + typedef typename boost::mpl::transform< + arg_types, + function_types, + boost::fusion::result_of::make_pair + >::type pair_list; + typedef typename boost::fusion::result_of::as_map::type function_map; + + // Maps from argument type to the runtime function object that can deal with it + function_map fmap; + + template + Result apply_helper(const T& object, boost::mpl::true_) const { + return boost::fusion::at_key(fmap)(object); + } + + template + Result apply_helper(const T& object, boost::mpl::false_) const { + return Result(); + } + + BOOST_MOVABLE_BUT_NOT_COPYABLE(generic_visitor) + +public: + generic_visitor(BOOST_RV_REF(type) other) + : + fmap(boost::move(other.fmap)) + { + } + generic_visitor(Functions... functions) + : + fmap(boost::fusion::as_map(boost::fusion::transform(boost::fusion::make_vector(functions...), pair_maker()))) + { + } + + template + Result operator()(const T& object) const { + typedef typename boost::fusion::result_of::has_key::type correct_key; + BOOST_STATIC_ASSERT_MSG(correct_key::value, + "make_visitor called without specifying handlers for all required types"); + return apply_helper(object, correct_key()); + } +}; + +// A metafunction class for getting the return type of a function +struct function_return_extractor +{ + template + struct apply : boost::function_types::result_type::type> + { + }; +}; + +// A metafunction class that asserts the two arguments are the same and returns the first one +struct check_same +{ + template + struct apply + { + private: + BOOST_STATIC_ASSERT_MSG((boost::is_same::value), + "make_visitor called with functions of differing return types"); + public: + typedef Type1 type; + }; +}; + +// A metafunction template helper +template +struct expand_generic_visitor +{ + template + struct apply + { + typedef generic_visitor type; + }; +}; + +// A metafunction for getting the required generic_visitor type for the set of Functions +template +struct get_generic_visitor +{ +private: + typedef boost::mpl::vector function_types; + typedef typename boost::mpl::transform< + function_types, + boost::remove_const< boost::remove_reference > + >::type bare_function_types; + typedef typename boost::mpl::transform::type return_types; + +public: + // Set result_type to the return type of the first function + typedef typename boost::mpl::front::type result_type; + + typedef typename boost::mpl::unpack_args< + expand_generic_visitor + >::template apply::type type; + +private: + // Assert that every return type is the same as the first one + typedef typename boost::mpl::fold::type dummy; +}; + +// Accepts a set of functions and returns an object suitable for apply_visitor +template +auto make_visitor(BOOST_RV_REF(Functions)... functions) + -> typename detail::get_generic_visitor::type +{ + return typename detail::get_generic_visitor::type(boost::forward(functions)...); +} + +} + +template +auto match(Variant const& variant, Function1 function1, BOOST_RV_REF(Functions)... functions) + -> typename detail::get_generic_visitor::result_type +{ + return boost::apply_visitor(detail::make_visitor( + boost::forward(function1), + boost::forward(functions)...), variant); +} + +#endif -- 2.39.5