From f5396e9386d0ce7704fb6b34fdf43867a6f67ada Mon Sep 17 00:00:00 2001 From: badair Date: Wed, 23 Mar 2016 20:57:25 -0500 Subject: [PATCH] fully-fledged std::bind expression parser --- include/callable_traits/any_arg.hpp | 37 ++++ include/callable_traits/arity.hpp | 21 +- include/callable_traits/bind_args_t.hpp | 108 ---------- include/callable_traits/bind_expression.hpp | 193 ++++++++++++++++++ .../bind_expression_parser.hpp | 165 +++++++++++++++ include/callable_traits/callable_traits.hpp | 21 +- .../callable_traits/categorize_bind_arg.hpp | 27 +++ ...args_t_fwd.hpp => bind_expression_fwd.hpp} | 8 +- .../callable_traits/is_bind_expression.hpp | 31 +++ .../callable_traits/placeholder_routes.hpp | 38 ++-- include/callable_traits/prepend.hpp | 35 ++++ .../remove_duplicate_placeholders.hpp | 4 +- include/callable_traits/sort_tuple.hpp | 29 ++- include/callable_traits/traits.hpp | 1 + include/callable_traits/tuple_group_by.hpp | 122 +++++++++++ test/{bind_args.cpp => bind_expression.cpp} | 46 ++--- test/bind_expression_parser.cpp | 165 +++++++++++++++ 17 files changed, 844 insertions(+), 207 deletions(-) create mode 100644 include/callable_traits/any_arg.hpp delete mode 100644 include/callable_traits/bind_args_t.hpp create mode 100644 include/callable_traits/bind_expression.hpp create mode 100644 include/callable_traits/bind_expression_parser.hpp rename include/callable_traits/fwd/{bind_args_t_fwd.hpp => bind_expression_fwd.hpp} (67%) create mode 100644 include/callable_traits/is_bind_expression.hpp create mode 100644 include/callable_traits/prepend.hpp create mode 100644 include/callable_traits/tuple_group_by.hpp rename test/{bind_args.cpp => bind_expression.cpp} (73%) create mode 100644 test/bind_expression_parser.cpp diff --git a/include/callable_traits/any_arg.hpp b/include/callable_traits/any_arg.hpp new file mode 100644 index 0000000..123e56b --- /dev/null +++ b/include/callable_traits/any_arg.hpp @@ -0,0 +1,37 @@ + /*! +@file + +@copyright Barrett Adair 2015 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +*/ + +#ifndef CALLABLE_TRAITS_ANY_ARG_HPP +#define CALLABLE_TRAITS_ANY_ARG_HPP + +namespace callable_traits { + + namespace ctdetail { + + //any_arg is only used in unevaluated contexts + template + struct any_arg { + + template + operator T& () const; + + template + operator T&& () const; + + any_arg() = default; + +#if !defined(_MSC_VER) + template + any_arg(T&&...); +#endif //!defined(_MSC_VER) + }; + } +} + +#endif //CALLABLE_TRAITS_ANY_ARG_HPP \ No newline at end of file diff --git a/include/callable_traits/arity.hpp b/include/callable_traits/arity.hpp index 41f0975..0a3a650 100644 --- a/include/callable_traits/arity.hpp +++ b/include/callable_traits/arity.hpp @@ -12,32 +12,13 @@ Distributed under the Boost Software License, Version 1.0. #include #include +#include #include namespace callable_traits { namespace ctdetail { - //any_arg is only used in unevaluated contexts - template - struct any_arg { - - template - operator T& () const; - - template - operator T&& () const; - - any_arg() = default; - -#if !defined(_MSC_VER) - template - any_arg(T&&...); -#endif - }; - - - template struct max_args { static constexpr bool value = true; diff --git a/include/callable_traits/bind_args_t.hpp b/include/callable_traits/bind_args_t.hpp deleted file mode 100644 index 7651a86..0000000 --- a/include/callable_traits/bind_args_t.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/*! -Copyright (c) 2001-2004 Peter Dimov and Multi Media Ltd. -Copyright (c) 2001 David Abrahams -Copyright (c) 2005 Peter Dimov -Copyright (c) 2016 Barrett Adair - -Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) -*/ - -#ifndef CALLABLE_TRAITS_BIND_WRAPPER_HPP -#define CALLABLE_TRAITS_BIND_WRAPPER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace callable_traits { - - namespace ctdetail { - - template - struct compare_input_index { - static constexpr bool value = - PhRouteLeft::input_index < PhRouteRight::input_index; - }; - - template - struct compare_output_index { - static constexpr bool value = - PhRouteLeft::output_index < PhRouteRight::output_index; - }; - - template - struct placeholders_mapped_to_original_signature; - - template - struct placeholders_mapped_to_original_signature< - PhRoutesTuple, OriginalArgsTuple, std::index_sequence> { - - //this is where the magic happens - using type = std::tuple< - typename std::tuple_element< - std::tuple_element::type::output_index, - OriginalArgsTuple - >::type... - >; - }; - - template - struct placeholders_mapped_to_original_signature< - PhRoutesTuple, std::tuple<>, std::index_sequence> { - using type = std::tuple<>; - }; - - template - struct placeholders_mapped_to_original_signature< - T, std::tuple, std::index_sequence> { - using type = std::tuple; - }; - - template - struct bind_args_t { - - private: - - using bind_args_tuple = std::tuple< - typename categorize_bind_arg::type... - >; - - using placeholder_route_map = typename placeholder_routes< - bind_args_tuple - >::type; - - using sorted_placeholder_routes = - typename remove_duplicate_placeholders< - sort_tuple< - placeholder_route_map, - compare_input_index - > - >::type; - - using placeholder_count = std::tuple_size< - sorted_placeholder_routes - >; - - using original_args = typename traits::arg_types; - - public: - - using type = typename placeholders_mapped_to_original_signature< - sorted_placeholder_routes, - original_args, - std::make_index_sequence - >::type; - }; - } -} - -#endif \ No newline at end of file diff --git a/include/callable_traits/bind_expression.hpp b/include/callable_traits/bind_expression.hpp new file mode 100644 index 0000000..3e4c95a --- /dev/null +++ b/include/callable_traits/bind_expression.hpp @@ -0,0 +1,193 @@ +/*! +Copyright (c) 2001-2004 Peter Dimov and Multi Media Ltd. +Copyright (c) 2001 David Abrahams +Copyright (c) 2005 Peter Dimov +Copyright (c) 2016 Barrett Adair + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +*/ + +#ifndef CALLABLE_TRAITS_BIND_EXPRESSION_HPP +#define CALLABLE_TRAITS_BIND_EXPRESSION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace callable_traits { + + namespace ctdetail { + + template + struct compare_ph_value { + static constexpr bool value = + PhRouteLeft::ph_value < PhRouteRight::ph_value; + }; + + template + struct placeholders_mapped_to_original_signature; + + template + struct placeholders_mapped_to_original_signature< + PhRoutesTuple, OriginalArgsTuple, std::index_sequence> { + + //this is where the magic happens + using type = std::tuple< + typename std::tuple_element< + std::tuple_element::type::original_arg_index, + OriginalArgsTuple + >::type... + >; + }; + + template + struct placeholders_mapped_to_original_signature< + PhRoutesTuple, std::tuple<>, std::index_sequence> { + using type = std::tuple<>; + }; + + template + struct placeholders_mapped_to_original_signature< + T, std::tuple, std::index_sequence> { + using type = std::tuple; + }; + + template struct bind_expressions_filter; + + template <> struct bind_expressions_filter<> { using type = std::tuple<>; }; + + template + struct bind_expressions_filter { + using type = typename bind_expressions_filter::type; + }; + + template + struct bind_expressions_filter< + bind_expression, Tail... + > { + using type = typename prepend< + bind_expression, + typename bind_expressions_filter::type + >::type; + }; + + template + struct remove_non_bind_expressions { + using type = typename bind_expressions_filter::type; + }; + + template + struct is_empty_tuple { + using value_type = std::false_type; + }; + + template<> + struct is_empty_tuple> { + using value_type = std::true_type; + }; + + template + struct is_not_empty_tuple { + using value_type = std::true_type; + }; + + template<> + struct is_not_empty_tuple> { + using value_type = std::false_type; + }; + + template + struct flatten_bind_expressions; + + template + struct flatten_bind_expressions< + std::tuple, + std::true_type + > { + using type = decltype(std::tuple_cat( + std::declval::type>()... + )); + }; + + template + struct flatten_bind_expressions< + BindExpr, + typename is_not_empty_tuple::value_type + > { + using type = typename prepend< + BindExpr, + typename flatten_bind_expressions< + typename BindExpr::inner_bind_expressions + >::type + >::type; + }; + + //base case - bind expression has no inner bind expressions + template + struct flatten_bind_expressions< + BindExpr, + typename is_empty_tuple::value_type + > { + using type = std::tuple; + }; + + template + struct bind_expression { + + using bind_args_tuple = std::tuple< + typename categorize_bind_arg::type... + >; + + using inner_bind_expressions = + typename remove_non_bind_expressions::type; + + using has_inner_bind_expressions = std::is_same< + inner_bind_expressions, std::tuple<> + >; + + using flattened_bind_expressions = + typename flatten_bind_expressions::type; + + using placeholder_route_map = typename placeholder_routes< + bind_expression, + bind_args_tuple + >::type; + + using sorted_placeholder_routes = + typename remove_duplicate_placeholders< + sort_tuple< + placeholder_route_map, + compare_ph_value + > + >::type; + + using placeholder_count = std::tuple_size< + sorted_placeholder_routes + >; + + using original_args = typename traits::arg_types; + + /*using arg_types = typename placeholders_mapped_to_original_signature< + sorted_placeholder_routes, + original_args, + std::make_index_sequence + >::type;*/ + + using return_type = typename traits::return_type; + }; + + } +} + +#endif \ No newline at end of file diff --git a/include/callable_traits/bind_expression_parser.hpp b/include/callable_traits/bind_expression_parser.hpp new file mode 100644 index 0000000..2fefb30 --- /dev/null +++ b/include/callable_traits/bind_expression_parser.hpp @@ -0,0 +1,165 @@ +/*! +Copyright (c) 2001-2004 Peter Dimov and Multi Media Ltd. +Copyright (c) 2001 David Abrahams +Copyright (c) 2005 Peter Dimov +Copyright (c) 2016 Barrett Adair + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +*/ + +#ifndef CALLABLE_TRAITS_BIND_EXPRESSION_PARSER_HPP +#define CALLABLE_TRAITS_BIND_EXPRESSION_PARSER_HPP + +#include +#include +#include +#include +#include +#include + +namespace callable_traits { + + namespace ctdetail { + + template + struct combined_placeholder_routes; + + template + struct combined_placeholder_routes> { + + using placeholder_route_map = decltype(std::tuple_cat(std::declval< + typename placeholder_routes< + BindExprs, + typename BindExprs::bind_args_tuple + >::type>()...)); + + using type = sort_tuple< + placeholder_route_map, + compare_ph_value + >; + }; + + template + struct ph_value_wrapper { + static constexpr const auto value = T::ph_value; + }; + + template struct filter_invalid_args; + + template <> struct filter_invalid_args<> { + using type = std::tuple<>; + }; + + template + struct filter_invalid_args { + + static constexpr const auto is_legal_arg = + !std::is_same>{} + && !std::is_same{} + && !std::is_same{}; + + using type = typename std::conditional< + is_legal_arg, + typename prepend::type>::type, + typename filter_invalid_args::type + >::type; + }; + + template + struct valid_args; + + template + struct valid_args> { + using filtered_args = typename filter_invalid_args::type; + using type = typename std::conditional< + std::is_same>::value, + std::tuple, + filtered_args + >::type; + }; + + template struct common_type; + + template + struct common_type { + using type = T; + }; + + template + struct common_type { + + using type = typename std::conditional< + std::is_convertible::value, + U, + typename std::conditional< + std::is_convertible::value, + T, + decltype(true ? std::declval() : std::declval()) + >::type + >::type; + }; + + template + struct common_type { + using type = typename common_type::type, V...>::type; + }; + + template + struct custom_common_type; + + template + struct custom_common_type> { + using type = typename common_type::type; + }; + + template + struct common_expected_arg_types; + + template + struct common_expected_arg_types, OtherGroupTuples...>> { + + using expected_types = std::tuple< + typename std::tuple_element< + PhRoutes::original_arg_index, + typename PhRoutes::expression::original_args + >::type... + >; + + using type = typename prepend< + typename custom_common_type::type>::type, + typename common_expected_arg_types>::type + >::type; + }; + + template<> + struct common_expected_arg_types> { + using type = std::tuple<>; + }; + + template + struct bind_expression_parser; + + template + struct bind_expression_parser> { + + using root_expression = bind_expression; + + using flattened_bind_expressions = + typename root_expression::flattened_bind_expressions; + + using placeholder_routes = typename combined_placeholder_routes< + flattened_bind_expressions + >::type; + + using grouped_placeholders = tuple_group_by< + placeholder_routes, + ph_value_wrapper + >; + + using arg_types = typename common_expected_arg_types::type; + }; + } +} + +#endif \ No newline at end of file diff --git a/include/callable_traits/callable_traits.hpp b/include/callable_traits/callable_traits.hpp index 5622e5c..186c4a4 100644 --- a/include/callable_traits/callable_traits.hpp +++ b/include/callable_traits/callable_traits.hpp @@ -23,7 +23,9 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include -#include +#include +#include +#include //todo remove? #include @@ -39,8 +41,15 @@ namespace callable_traits { return{}; } - template - using args = typename ctdetail::traits::arg_types; + template< + typename T, + typename U = typename std::remove_cv::type>::type + > + using args = typename std::conditional< + ctdetail::is_bind_expression::value, + ctdetail::bind_expression_parser, + ctdetail::traits + >::type::arg_types; template using arg_at = typename std::tuple_element>::type; @@ -116,8 +125,10 @@ namespace callable_traits { } template - auto bind_args(Callable, Args...) -> - typename ctdetail::bind_args_t::type; + auto bind_expr(Callable, Args...) -> + ctdetail::bind_expression { + return{}; + } } #endif \ No newline at end of file diff --git a/include/callable_traits/categorize_bind_arg.hpp b/include/callable_traits/categorize_bind_arg.hpp index 02aa459..d01d1f9 100644 --- a/include/callable_traits/categorize_bind_arg.hpp +++ b/include/callable_traits/categorize_bind_arg.hpp @@ -9,6 +9,9 @@ Distributed under the Boost Software License, Version 1.0. */ #include +#include +#include +#include #include namespace callable_traits { @@ -25,21 +28,45 @@ namespace callable_traits { bind_value, ph::value> >::type; + + using bind_expression = invalid_type; + using is_bind_expression = std::false_type; }; template struct categorize_bind_arg< bind_value > { using type = ctdetail::bind_value; + using bind_expression = invalid_type; + using is_bind_expression = std::false_type; }; template struct categorize_bind_arg< std::reference_wrapper > { using type = std::reference_wrapper; + using bind_expression = invalid_type; + using is_bind_expression = std::false_type; }; template struct categorize_bind_arg< ph > { using type = ph; + using bind_expression = invalid_type; + using is_bind_expression = std::false_type; + }; + + template + struct categorize_bind_arg> { + + using return_type = typename bind_expression::return_type; + + using type = typename std::conditional< + std::is_same::value, + any_arg<>, + return_type + >::type; + + using bind_expression = bind_expression; + using is_bind_expression = std::true_type; }; } } \ No newline at end of file diff --git a/include/callable_traits/fwd/bind_args_t_fwd.hpp b/include/callable_traits/fwd/bind_expression_fwd.hpp similarity index 67% rename from include/callable_traits/fwd/bind_args_t_fwd.hpp rename to include/callable_traits/fwd/bind_expression_fwd.hpp index 8ffbd8f..bcdf661 100644 --- a/include/callable_traits/fwd/bind_args_t_fwd.hpp +++ b/include/callable_traits/fwd/bind_expression_fwd.hpp @@ -5,15 +5,15 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) */ -#ifndef CALLABLE_TRAITS_FWD_BIND_ARGS_T_FWD_HPP -#define CALLABLE_TRAITS_FWD_BIND_ARGS_T_FWD_HPP +#ifndef CALLABLE_TRAITS_FWD_BIND_EXPRESSION_FWD_HPP +#define CALLABLE_TRAITS_FWD_BIND_EXPRESSION_FWD_HPP namespace callable_traits { namespace ctdetail { - + template - struct bind_args_t; + struct bind_expression; } } diff --git a/include/callable_traits/is_bind_expression.hpp b/include/callable_traits/is_bind_expression.hpp new file mode 100644 index 0000000..43c431e --- /dev/null +++ b/include/callable_traits/is_bind_expression.hpp @@ -0,0 +1,31 @@ + /*! +@file + +@copyright Barrett Adair 2015 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +*/ + +#ifndef CALLABLE_TRAITS_IS_BIND_EXPRESSION_HPP +#define CALLABLE_TRAITS_IS_BIND_EXPRESSION_HPP + +#include + +namespace callable_traits { + + namespace ctdetail { + + template + struct is_bind_expression { + static constexpr const bool value = false; + }; + + template + struct is_bind_expression> { + static constexpr const bool value = true; + }; + } +} + +#endif \ No newline at end of file diff --git a/include/callable_traits/placeholder_routes.hpp b/include/callable_traits/placeholder_routes.hpp index b4988a5..102d411 100644 --- a/include/callable_traits/placeholder_routes.hpp +++ b/include/callable_traits/placeholder_routes.hpp @@ -8,23 +8,27 @@ Distributed under the Boost Software License, Version 1.0. #ifndef CALLABLE_TRAITS_BIND_PLACEHOLDER_ROUTES_HPP #define CALLABLE_TRAITS_BIND_PLACEHOLDER_ROUTES_HPP +#include + namespace callable_traits { namespace ctdetail { - template + template struct ph_route { - static constexpr const auto output_index = OutputIndex; - static constexpr const auto input_index = InputIndex; + using expression = Expression; + static constexpr const auto original_arg_index = OriginalArgIndex; + static constexpr const auto ph_value = PhValue; }; - template struct argument_routing {}; + template struct argument_routing {}; - template - struct argument_routing, Tuple> { + template + struct argument_routing, Tuple> { using type = std::tuple< ph_route< + Expression, I, std::is_placeholder< typename std::tuple_element::type @@ -33,17 +37,6 @@ namespace callable_traits { >; }; - template struct prepend; - - template <> struct prepend<> { - using type = std::tuple<>; - }; - - template - struct prepend > { - using type = std::tuple; - }; - //base case template struct placeholder_routes_detail; template <> struct placeholder_routes_detail<> { @@ -54,7 +47,7 @@ namespace callable_traits { struct placeholder_routes_detail { //TODO - is there a faster way to do this? using type = typename std::conditional< - Head::input_index == 0, + Head::ph_value == 0, typename placeholder_routes_detail::type, typename prepend< Head, @@ -74,14 +67,15 @@ namespace callable_traits { std::is_placeholder::value < std::is_placeholder::value; }; - template struct placeholder_routes; - template <> struct placeholder_routes<> { using type = std::tuple<>; }; + template struct placeholder_routes; + template struct placeholder_routes { using type = std::tuple<>; }; - template - struct placeholder_routes > + template + struct placeholder_routes > { using routed_placeholders = typename placeholder_routes_detail< typename argument_routing< + Expression, std::make_index_sequence, std::tuple >::type diff --git a/include/callable_traits/prepend.hpp b/include/callable_traits/prepend.hpp new file mode 100644 index 0000000..b5fbc36 --- /dev/null +++ b/include/callable_traits/prepend.hpp @@ -0,0 +1,35 @@ +/*! +@copyright Barrett Adair 2016 + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +*/ + +#ifndef CALLABLE_TRAITS_PREPEND_HPP +#define CALLABLE_TRAITS_PREPEND_HPP + +#include + +namespace callable_traits { + + namespace ctdetail { + + template struct prepend; + + template <> struct prepend<> { + using type = std::tuple<>; + }; + + template + struct prepend > { + using type = std::tuple; + }; + + template + struct prepend > { + using type = private_tuple; + }; + } +} + +#endif \ No newline at end of file diff --git a/include/callable_traits/remove_duplicate_placeholders.hpp b/include/callable_traits/remove_duplicate_placeholders.hpp index e628c1c..b19b7ee 100644 --- a/include/callable_traits/remove_duplicate_placeholders.hpp +++ b/include/callable_traits/remove_duplicate_placeholders.hpp @@ -26,7 +26,7 @@ namespace callable_traits { template struct remove_duplicate_placeholders { using type = typename std::conditional< - PhRoute1::input_index == PhRoute2::input_index, + PhRoute1::ph_value == PhRoute2::ph_value, typename remove_duplicate_placeholders::type, typename prepend::type>::type >::type; @@ -36,7 +36,7 @@ namespace callable_traits { template struct remove_duplicate_placeholders { using type = typename std::conditional< - PhRoute1::input_index == PhRoute2::input_index, + PhRoute1::ph_value == PhRoute2::ph_value, std::tuple, std::tuple >::type; diff --git a/include/callable_traits/sort_tuple.hpp b/include/callable_traits/sort_tuple.hpp index 99957c4..5474166 100644 --- a/include/callable_traits/sort_tuple.hpp +++ b/include/callable_traits/sort_tuple.hpp @@ -18,17 +18,16 @@ namespace callable_traits { namespace ctdetail { - using index = std::size_t; using empty_seq = std::index_sequence<>; - template + template struct insert; // We did not find the insertion point; continue processing elements // recursively. - template + template struct insert, Right1, Right2, Right...> { using type = typename insert< @@ -40,22 +39,22 @@ namespace callable_traits { // We did not find the insertion point, but there is only one element // left. We insert at the end of the list, and we're done. - template + template struct insert, Last> { using type = std::index_sequence; }; // We found the insertion point, we're done. - template + template struct insert, Right...> { using type = std::index_sequence; }; - template + template struct insertion_sort; - template + template struct insertion_sort, T, Ts...> { static constexpr bool pred_result = Pred::template apply::value; using insert_result = typename insert< @@ -64,7 +63,7 @@ namespace callable_traits { using type = typename insertion_sort::type; }; - template + template struct insertion_sort { using type = typename insertion_sort< Pred, std::index_sequence, Ts... @@ -79,26 +78,26 @@ namespace callable_traits { template struct sort_indices; - template + template struct sort_indices> { using type = typename insertion_sort::type; }; template struct sort_impl { - static constexpr index len = std::tuple_size::value; + static constexpr std::size_t len = std::tuple_size::value; using indices = typename sort_indices>::type; using type = typename sort_impl::type; }; - template + template struct sort_impl> { using type = std::tuple::type...>; }; template class Pred> struct predicate { - template + template using apply = Pred< typename std::tuple_element::type, typename std::tuple_element::type diff --git a/include/callable_traits/traits.hpp b/include/callable_traits/traits.hpp index 9b73fee..ae558cb 100644 --- a/include/callable_traits/traits.hpp +++ b/include/callable_traits/traits.hpp @@ -19,6 +19,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include diff --git a/include/callable_traits/tuple_group_by.hpp b/include/callable_traits/tuple_group_by.hpp new file mode 100644 index 0000000..74c2b07 --- /dev/null +++ b/include/callable_traits/tuple_group_by.hpp @@ -0,0 +1,122 @@ +/*! +@file + +@coyright Modified Work Barrett Adair 2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +*/ + +#ifndef CALLABLE_TRAITS_TUPLE_GROUP_BY_HPP +#define CALLABLE_TRAITS_TUPLE_GROUP_BY_HPP + +#include +#include +#include +#include + +namespace callable_traits { + + namespace ctdetail { + + template class Pred> + struct group_by_value { + static constexpr auto value = Pred::value; + }; + + template class Pred> + struct distinct_group_by_values; + + template class Pred> + struct distinct_group_by_values, Pred> { + using type = std::tuple>; + }; + + template class Pred> + struct distinct_group_by_values, Pred> { + + static constexpr const auto is_in_same_group = + Pred::value == Pred::value; + + using next = typename distinct_group_by_values, Pred>::type; + + using type = typename std::conditional< + is_in_same_group, + next, + typename prepend, next>::type + >::type; + }; + + template struct group_by_filter_impl; + + template struct group_by_filter_impl { + using type = std::tuple<>; + }; + + template + struct group_by_filter_impl { + using type = typename std::conditional< + decltype(std::declval()(std::declval()))::value, + typename prepend::type>::type, + typename group_by_filter_impl::type + >::type; + }; + + template + struct group_by_filter; + + template + struct group_by_filter> { + using type = typename group_by_filter_impl::type; + }; + + template class Pred, std::size_t I> + struct filter_predicate { + + using compare_against = + typename std::tuple_element::type; + + template + auto operator()(T) -> + std::integral_constant::value == compare_against::value>; + }; + + template class Pred, typename ValuesSeq> + struct group_by_impl; + + template class Pred, std::size_t... I> + struct group_by_impl> { + + using type = std::tuple< + typename group_by_filter< + filter_predicate, + Tup + >::type... + >; + }; + + template class Pred> + struct group_by_t { + + template + struct sort_predicate { + static constexpr const auto left = Pred::value; + static constexpr const auto right = Pred::value; + static constexpr const bool value = left < right; + }; + + using group_by_values = typename distinct_group_by_values::type; + + using type = typename group_by_impl< + sort_tuple, + group_by_values, + Pred, + std::make_index_sequence::value> + >::type; + }; + + template class Pred> + using tuple_group_by = typename group_by_t::type; + } +} + +#endif \ No newline at end of file diff --git a/test/bind_args.cpp b/test/bind_expression.cpp similarity index 73% rename from test/bind_args.cpp rename to test/bind_expression.cpp index ca1d703..4eed23d 100644 --- a/test/bind_args.cpp +++ b/test/bind_expression.cpp @@ -71,7 +71,7 @@ apply(F&& f, Tuple&& t) { std::forward(f), std::forward(t), std::make_index_sequence< - std::tuple_size>::value + std::tuple_size>::value >{} ); } @@ -89,61 +89,45 @@ int main() { assert(letters(a, b, c, d, e, f, g) == "ABCDEFG"); assert(ordered_letters(a, b, c, d, e, f, g) == "ABCDEFG"); + { - using args = decltype(ct::bind_args( - &ordered_letters, _1, _2, _3, _4, _5, _6, _7)); + auto expr = ct::bind_expr(&ordered_letters, _1, _2, _3, _4, _5, _6, _7); + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same{}); - auto test = std::bind(&ordered_letters, _1, _2, _3, _4, _5, _6, _7); assert(apply(test, expected_args{}) == "ABCDEFG"); - } { - using args = decltype(ct::bind_args( - &ordered_letters, a, b, c, _1, _2, _3, _4)); + } { + auto expr = ct::bind_expr(&ordered_letters, a, b, c, _1, _2, _3, _4); + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same{}); - auto test = std::bind(&ordered_letters, a, b, c, _1, _2, _3, _4); assert(apply(test, expected_args{}) == "ABCDEFG"); } { - using args = decltype(ct::bind_args( - &ordered_letters, _7, _6, _5, _4, _3, _2, _1)); + auto expr = ct::bind_expr(&ordered_letters, _7, _6, _5, _4, _3, _2, _1); + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same{}); - auto test = std::bind(&ordered_letters, _7, _6, _5, _4, _3, _2, _1); assert(apply(test, expected_args{}) == "ABCDEFG"); } { - using args = decltype(ct::bind_args( - &ordered_letters, a, b, c, _4, _3, _2, _1)); + auto expr = ct::bind_expr(&ordered_letters, a, b, c, _4, _3, _2, _1); + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same{}); - auto test = std::bind(&ordered_letters, a, b, c, _4, _3, _2, _1); assert(apply(test, expected_args{}) == "ABCDEFG"); } { - using args = decltype(ct::bind_args( - &ordered_letters, _4, _3, _2, _1, e, f, g)); + auto expr = ct::bind_expr(&ordered_letters, _4, _3, _2, _1, e, f, g); + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same{}); - auto test = std::bind(&ordered_letters, _4, _3, _2, _1, e, f, g); assert(apply(test, expected_args{}) == "ABCDEFG"); } { - //These aren't valid binds. Here we are testing the - //correctness of the arg_types alias in binding_wrapper. - using args = decltype(ct::bind_args( - &ordered_letters, _1, _1, _3, _3, _2, _1, _2)); - using expected_args = std::tuple; - CT_ASSERT(std::is_same{}); - } { - using args = decltype(ct::bind_args( - &ordered_letters, _4, _1, _2, _3, _1, _2, _4)); - using expected_args = std::tuple; - CT_ASSERT(std::is_same{}); - } { - using args = decltype(ct::bind_args( - &letters, _1, _1, _3, _3, _2, a, b)); + auto expr = ct::bind_expr(&letters, _1, _1, _3, _3, _2, a, b); + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same{}); } diff --git a/test/bind_expression_parser.cpp b/test/bind_expression_parser.cpp new file mode 100644 index 0000000..7768d45 --- /dev/null +++ b/test/bind_expression_parser.cpp @@ -0,0 +1,165 @@ +/* + +Copyright Barrett Adair 2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CT_ASSERT +#define CT_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) +#endif //CT_ASSERT + +using namespace std::placeholders; +namespace ct = callable_traits; + +struct Letter { + virtual operator const char*() const volatile = 0; +}; + +#define DEFINE_TEST_LETTER(L) \ +struct L : Letter { operator const char*() const volatile override { return #L; } } + +DEFINE_TEST_LETTER(A); +DEFINE_TEST_LETTER(B); +DEFINE_TEST_LETTER(C); +DEFINE_TEST_LETTER(D); +DEFINE_TEST_LETTER(E); +DEFINE_TEST_LETTER(F); +DEFINE_TEST_LETTER(G); + +// functions `ordered_letters`, `BEEF_returns_D`, `BEEF_returns_G`, +// and `BEEF_returns_B` are used to set up a complex bind expression +// with ct::bind_expr + +auto ordered_letters(A a, B b, C c, D d, E e, F f, G g) { + auto ss = std::stringstream{}; + ss << a << b << c << d << e << f << g; + return ss.str(); +} + +auto BEEF_returns_D(B, E, E, F) { + return D{}; +} + +auto BEEF_returns_G(B, E, E, F) { + return G{}; +} + +auto BEEF_returns_B(B, E, E, F) { + return B{}; +} + +template +constexpr decltype(auto) +apply_helper(F&& f, Tuple&& t, std::index_sequence) { + return std::forward(f)(std::get(std::forward(t))...); +} + +//used to apply the expected_args tuple to std::bind +template +constexpr decltype(auto) +apply(F&& f, Tuple&& t) { + return apply_helper( + std::forward(f), + std::forward(t), + std::make_index_sequence< + std::tuple_size>::value + >{} + ); +} + +const auto a = A{}; +const auto b = B{}; +const auto c = C{}; +const auto d = D{}; +const auto e = E{}; +const auto f = F{}; +const auto g = G{}; + +// lets us create a complex bind expression with both +// `std::bind` and `ct::bind_expr` +#define BIND_WITH(bind_name) \ + bind_name(&ordered_letters, \ + _1, \ + _2, \ + _3, \ + bind_name(&BEEF_returns_D, \ + _2, \ + e, \ + _4, \ + _7 \ + ), \ + _5, \ + _6, \ + bind_name(&BEEF_returns_G, \ + bind_name(&BEEF_returns_B, \ + b, \ + _10, \ + e, \ + f \ + ), \ + _9, \ + e, \ + _8 \ + ) \ + ) \ +/**/ + +template +void check_expression_flattening(); + +int main() { + + assert(ordered_letters(a, b, c, d, e, f, g) == "ABCDEFG"); + + using bind_expr = decltype(BIND_WITH(ct::bind_expr)); + + check_expression_flattening(); + + using args = ct::args; + + // these are the argument types as dictated by + // the bind expression's placeholders + using expected_args = std::tuple; + + CT_ASSERT(std::is_same{}); + + auto runtime_test = BIND_WITH(std::bind); + assert(apply(runtime_test, expected_args{}) == "ABCDEFG"); + + return 0; +} + +template +void check_expression_flattening() { + using inner3 = decltype(ct::bind_expr(&BEEF_returns_B, b, _10, e, f)); + using inner2 = decltype(ct::bind_expr(&BEEF_returns_G, inner3{}, _9, e, _8)); + using inner1 = decltype(ct::bind_expr(&BEEF_returns_D, _2, e, _4, _7)); + + using bind_expr_check = decltype(ct::bind_expr( + &ordered_letters, _1, _2, _3, + inner1{}, + _5, _6, + inner2{} + )); + + // this assert makes sure the BIND_WITH macro (defined above) + // hasn't been modified out of sync with this function definition + CT_ASSERT(std::is_same{}); + + using flattened_exprs = typename BindExpr::flattened_bind_expressions; + using expected_exprs = std::tuple; + + CT_ASSERT(std::is_same{}); +} \ No newline at end of file