From f888183eb3e05ee2040b9a8b2aefc18f203042db Mon Sep 17 00:00:00 2001 From: badair Date: Wed, 6 Apr 2016 15:58:47 -0500 Subject: [PATCH] cleaning up interface, adding std_function example --- doc/callable_traits.qbk | 39 ++++--- example/ambiguity.cpp | 2 +- example/bind/example1.cpp | 77 +++++++++++++ example/bind/example2.cpp | 57 ++++++++++ example/bind_expr/example1.cpp | 93 ---------------- example/bind_expr/example2.cpp | 65 ----------- .../function_type.cpp} | 8 +- example/intro.cpp | 33 ++---- example/std_function/example.cpp | 52 +++++++++ example/std_function/make_function.hpp | 40 +++++++ example/void.cpp | 4 +- .../detail/bind_expression.hpp | 104 +++++++++++++----- .../detail/bind_expression_traits.hpp | 2 +- .../detail/categorize_bind_arg.hpp | 26 ++--- include/callable_traits/detail/function.hpp | 2 + .../detail/function_object.hpp | 5 +- include/callable_traits/detail/pmd.hpp | 7 +- include/callable_traits/detail/pmf.hpp | 8 +- include/callable_traits/interface.hpp | 13 ++- qtcreator/callable_traits.pro | 2 +- qtcreator/doc/doc.pro | 4 + .../tests_and_examples/tests_and_examples.pro | 24 ++-- test/best_match_bind.cpp | 46 +++----- test/bind_expression.cpp | 56 ++++++---- test/bind_expression_parser.cpp | 50 +++------ test/detail/flatten_bind_expressions.cpp | 73 ++++++++++++ test/varargs.cpp | 6 +- 27 files changed, 541 insertions(+), 357 deletions(-) create mode 100644 example/bind/example1.cpp create mode 100644 example/bind/example2.cpp delete mode 100644 example/bind_expr/example1.cpp delete mode 100644 example/bind_expr/example2.cpp rename example/{signature/signature.cpp => function_type/function_type.cpp} (89%) create mode 100644 example/std_function/example.cpp create mode 100644 example/std_function/make_function.hpp create mode 100644 qtcreator/doc/doc.pro create mode 100644 test/detail/flatten_bind_expressions.cpp diff --git a/doc/callable_traits.qbk b/doc/callable_traits.qbk index 96d70bd..aea9186 100644 --- a/doc/callable_traits.qbk +++ b/doc/callable_traits.qbk @@ -10,6 +10,7 @@ ] [id callable_traits] [last-revision $Date$] + [quickbook 1.5] ] [section:moti Motivation] @@ -29,14 +30,14 @@ The complexity of callable types in C++ is extensive: *qualified overloads of member functions: `const`, `volatile`, `&`, `&&` *C-style varargs (`...`) *calling conventions (`__cdecl`, `__stdcall`, `__fastcall`, `pascal`, etc.) -*`noexcept` (part of the function type system in C++17) +*`noexcept` ([@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html part of the function type system in C++17]) -CallableTraits provides a comprehensive, uniform and modern type-level interface for the manipulation and inspection of callable types in C++. CallableTraits aims to provide such an exhaustive interface for the features listed above that the libary writer will *never again be required to specialize templates for callable types*. In other words, library writers should never need to write a template like this one:, for *any* reason: +CallableTraits provides a comprehensive, uniform, and modern type-level interface for the manipulation and inspection of callable types in C++. CallableTraits aims to provide such an exhaustive interface for the features listed above that libary writers will *never again be required to specialize templates for callable types*. With CallableTraits, ibrary writers will never need to write a template like this one, for *any* reason: template - struct foo{ - //... - }; + struct foo{ + //... + }; Several library solutions exist to manipulate these types, or to abstract away their complexities. However, in the opinion of the author, these solutions are often inflexible and lacking in features, especially in regard to function objects and lambdas. In many cases, the CallableTraits interface allow authors of generic code to treat these types as interchangeable: function objects, function pointers, function references, function types, abominable function types, member function pointers, member data pointers, and references/pointers/smart pointers to all of the above (whenever applicable). @@ -52,6 +53,8 @@ CallableTraits contains the following type traits and metafunctions (psuedo-code * `is_overloaded()` * `has_varargs(T&&)` * `has_varargs()` +* `has_void_return(T&&)` +* `has_void_return()` * `arity(T&&)` * `arity()` * `min_arity(T&&)` @@ -77,34 +80,38 @@ CallableTraits contains the following type traits and metafunctions (psuedo-code * `args` * `arg_at` -* `signature` -* `qualified_signature` -* `common_signature` +* `function_type` +* `qualified_function_type` * `result_of` -* `remove_const_qualifier` -* `remove_volatile_qualifier` -* `remove_cv_qualifiers` -* `remove_reference_qualifiers` -* `remove_varargs` +* `apply_return` +* `apply_member_pointer` +* `remove_member_pointer` * `add_const_qualifier` +* `remove_const_qualifier` * `add_volatile_qualifier` +* `remove_volatile_qualifier` * `add_cv_qualifiers` +* `remove_cv_qualifiers` * `add_lvalue_qualifier` * `add_rvalue_qualifier` +* `remove_reference_qualifiers` * `add_varargs` +* `remove_varargs` [*Other] -* `bind_expr(T, Args...)` +* `bind(T&&, Args&&...)` + +Note: CallableTraits currently does little with regard to calling conventions, and no features are implemented for C++17's `noexcept`. [endsect] [section:tuto Overview] -These are the +These are the To start using CallableTraits, include the header file: #include -[endsect] \ No newline at end of file +[endsect] diff --git a/example/ambiguity.cpp b/example/ambiguity.cpp index e49b3f2..d7ea130 100644 --- a/example/ambiguity.cpp +++ b/example/ambiguity.cpp @@ -23,7 +23,7 @@ int main() { using expect = std::tuple; static_assert(std::is_same{}, ""); } { - using test = ct::no_sfinae::signature; + using test = ct::no_sfinae::function_type; using expect = ct::unknown(ct::unknown); static_assert(std::is_same{}, ""); } { diff --git a/example/bind/example1.cpp b/example/bind/example1.cpp new file mode 100644 index 0000000..05159af --- /dev/null +++ b/example/bind/example1.cpp @@ -0,0 +1,77 @@ +/* + +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 + +struct Vampire {}; +struct Robot {}; +struct Animal {}; +struct Dog : Animal {}; +struct Poodle : Dog {}; +struct ScaryMonster : Vampire, Robot, Poodle {}; + +auto vampire_to_robot(Vampire) { + return Robot{}; +} + +auto robot_to_dog = [](Robot){ + return Dog{}; +}; + +struct converter { + auto dog_to_vampire(Dog) { + return Vampire{}; + } +}; + +int take_all(Vampire, Robot, Animal, Dog, Poodle, ScaryMonster) { + return 0; +} + +using namespace std::placeholders; +namespace ct = callable_traits; + +int main() { + + auto b = + ct::bind(&take_all, + ct::bind(&converter::dog_to_vampire, + converter{}, + ct::bind(robot_to_dog, + ct::bind(&vampire_to_robot, _1) + ) + ), + ct::bind(&vampire_to_robot, _3), + Animal{}, + _1, + _2, + _1 + ); + + // the last _1 placeholder in this bind expression forces + // all other _1 slots to accept ScaryMonster, the + // narrowest of all _1 parameters. + + { + using args = ct::args; + using expect = std::tuple; + static_assert(std::is_same::value, ""); + } { + using type = ct::function_type; + using expect = int(ScaryMonster, Poodle, Vampire); + static_assert(std::is_same::value, ""); + } + + assert(b(ScaryMonster{}, Poodle{}, Vampire{}) == 0); + + return 0; +} diff --git a/example/bind/example2.cpp b/example/bind/example2.cpp new file mode 100644 index 0000000..0d6740c --- /dev/null +++ b/example/bind/example2.cpp @@ -0,0 +1,57 @@ +/* + +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 + +struct Vampire {}; +struct Robot {}; +struct Animal {}; +struct Dog : Animal {}; +struct Poodle : Dog {}; +struct ScaryMonster : Poodle, Robot, Vampire {}; + +auto take_vampire(const Vampire&) { return 0; } +auto take_robot(const Robot&) { return 0; } +auto take_dog(const Dog&) { return 0; } +auto take_scary_monster(const ScaryMonster&) { return 0; } + +int f(int, int, int, int) { return 0; } + +using namespace std::placeholders; +namespace ct = callable_traits; + +int main() { + + ScaryMonster monster{}; + + auto b = ct::bind( + &f, + ct::bind(&take_vampire, _1), + ct::bind(&take_robot, _1), + ct::bind(&take_dog, _1), + ct::bind(&take_scary_monster, _1) + ); + + { + using args = ct::args; + using expect = std::tuple; + static_assert(std::is_same::value, ""); + } { + using type = ct::function_type; + using expect = int(const ScaryMonster&); + static_assert(std::is_same::value, ""); + } + + assert(b(monster) == 0); + + return 0; +} diff --git a/example/bind_expr/example1.cpp b/example/bind_expr/example1.cpp deleted file mode 100644 index 7c9d979..0000000 --- a/example/bind_expr/example1.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - -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 - -struct Vampire {}; -struct Robot {}; -struct Animal {}; -struct Dog : Animal {}; -struct Poodle : Dog {}; -struct VampireRobotPoodle : Vampire, Robot, Poodle {}; - -auto vampire_to_robot(Vampire) { - return Robot{}; -} - -auto robot_to_dog = [](Robot){ - return Dog{}; -}; - -struct converter { - auto dog_to_vampire(Dog) { - return Vampire{}; - } -}; - -int take_all(Vampire, Robot, Animal, Dog, Poodle, VampireRobotPoodle) { - return 0; -} - -using namespace std::placeholders; -namespace ct = callable_traits; - -int main() { - - using bind_expr = decltype( - ct::bind_expr(&take_all, - ct::bind_expr(&converter::dog_to_vampire, - converter{}, - ct::bind_expr(robot_to_dog, - ct::bind_expr(&vampire_to_robot, _1) - ) - ), - ct::bind_expr(&vampire_to_robot, _3), - Animal{}, - _1, - _2, - _1 - ) - ); - - auto bind_obj = - std::bind(&take_all, - std::bind(&converter::dog_to_vampire, - converter{}, - std::bind(robot_to_dog, - std::bind(&vampire_to_robot, _1) - ) - ), - std::bind(&vampire_to_robot, _3), - Animal{}, - _1, - _2, - _1 - ); - - // the last _1 placeholder in this bind expression forces all other - // _1 slots to accept VampireRobotPoodle, the narrowest of the bunch. - - using bind_args = ct::args; - using expected_args = std::tuple; - static_assert(std::is_same::value, ""); - - using bind_signature = ct::signature; - using expected_signature = int(VampireRobotPoodle, Poodle, Vampire); - static_assert(std::is_same::value, ""); - - assert(bind_obj(VampireRobotPoodle{}, Poodle{}, Vampire{}) == 0); - - auto fn = std::function{ bind_obj }; - assert(fn(VampireRobotPoodle{}, Poodle{}, Vampire{}) == 0); - - return 0; -} \ No newline at end of file diff --git a/example/bind_expr/example2.cpp b/example/bind_expr/example2.cpp deleted file mode 100644 index 6e753ec..0000000 --- a/example/bind_expr/example2.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - -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 - -struct Vampire {}; -struct Robot {}; -struct Animal {}; -struct Dog : Animal {}; -struct Poodle : Dog {}; -struct VampireRobotPoodle : Poodle, Robot, Vampire {}; - -auto take_vampire(const Vampire&) { return 0; } -auto take_robot(const Robot&) { return 0; } -auto take_dog(const Dog&) { return 0; } -auto take_vampire_robot_poodle(const VampireRobotPoodle&) { return 0; } - -int f(int, int, int, int) { return 0; } - -using namespace std::placeholders; -namespace ct = callable_traits; - -int main() { - - using bind_expr = decltype(ct::bind_expr( - &f, - ct::bind_expr(&take_vampire, _1), - ct::bind_expr(&take_robot, _1), - ct::bind_expr(&take_dog, _1), - ct::bind_expr(&take_vampire_robot_poodle, _1) - )); - - auto bind_obj = std::bind( - &f, - std::bind(&take_vampire, _1), - std::bind(&take_robot, _1), - std::bind(&take_dog, _1), - std::bind(&take_vampire_robot_poodle, _1) - ); - - using bind_args = ct::args; - using expected_args = std::tuple; - static_assert(std::is_same::value, ""); - - using bind_signature = ct::signature; - using expected_signature = int(const VampireRobotPoodle&); - static_assert(std::is_same::value, ""); - - VampireRobotPoodle vampire_robot_poodle; - assert(bind_obj(vampire_robot_poodle) == 0); - - std::function func = bind_obj; - assert(func(vampire_robot_poodle) == 0); - - return 0; -} \ No newline at end of file diff --git a/example/signature/signature.cpp b/example/function_type/function_type.cpp similarity index 89% rename from example/signature/signature.cpp rename to example/function_type/function_type.cpp index 104ec9f..36502a3 100644 --- a/example/signature/signature.cpp +++ b/example/function_type/function_type.cpp @@ -15,9 +15,9 @@ using expect = void(int, float&, const char*); template void test(){ - // this example shows how callable_traits::signature + // this example shows how callable_traits::function_type // bevaves consistently for many different types - using args = ct::signature; + using args = ct::function_type; static_assert(std::is_same{}, ""); } @@ -31,12 +31,12 @@ int main() { test(); test(); - struct foo; + /*struct foo; using pmf = void(foo::*)(int, float&, const char*); test(); test(); test(); - test(); + test();*/ using function_ptr = void(*)(int, float&, const char*); test(); diff --git a/example/intro.cpp b/example/intro.cpp index be32ff2..9bf763e 100644 --- a/example/intro.cpp +++ b/example/intro.cpp @@ -43,10 +43,10 @@ int main() { using return_type = ct::result_of; static_assert(std::is_same::value, ""); - // callable_traits::signature yields a plain function type - using signature = ct::signature; - using expected_signature = void(int, int&&, const int&, void*); - static_assert(std::is_same::value, ""); + // callable_traits::function_type yields a plain function type + using function_type = ct::function_type; + using expected_function_type = void(int, int&&, const int&, void*); + static_assert(std::is_same::value, ""); // when trait information can be conveyed in an integral_constant, // callable_traits uses constexpr functions instead of template aliases. @@ -60,7 +60,7 @@ int main() { // need to worry about reference collapsing or decltype when dealing with // universal references to callables. Still, you don't need an instance, // because CallableTraits provides non-deduced function templates for - // all constexpr functions besides can_invoke and bind_expr, which + // all constexpr functions besides can_invoke and bind, which // model std::invoke and std::bind, respectively (more on these below). // Here's an example of the non-deduced functions, which take an explicit // type argument. We'll ignore these for the rest of the example. @@ -88,34 +88,23 @@ int main() { static_assert(!ct::can_invoke(foo{}, nullptr), ""); // error: std::invoke(foo{}, nullptr); - // callable_traits::bind_expr is a compile-time bind expression parser, + // callable_traits::bind is a compile-time bind expression parser, // very loosely based on the Boost.Bind implementation. Nested bind - // expressions are fully supported. The return type of bind_expr only + // expressions are fully supported. The return type of bind only // contains type information, but can still be used in an evaluated // context. The return type can be treated like a callable type when passed - // to result_of, signature, args, or arg_at template aliases. - using bind_expression = decltype(ct::bind_expr(foo{}, _1, _1, _1)); + // to result_of, function_type, args, or arg_at template aliases. + auto b = ct::bind(foo{}, _1, _1, _1); - // Unfortunately, we can't do type manipulations with std::bind directly, - // because the ISO C++ standard says very little about the return type of - // std::bind. The purpose of callable_traits::bind_expr is to undo some of - // the arbitrary black-boxing that std::bind incurs. - auto bind_obj = std::bind(foo{}, _1, _1, _1); - - // Here, int is chosen as the expected argument for the bind expression + // Here, int&& is chosen as the expected argument for the bind expression // because it's the best fit for all three placeholder slots. Behind // the scenes, this is determined by a cartesian product of parameter // conversion combinations that are represented by the reused placeholders. static_assert(std::is_same< - ct::args, + ct::args, std::tuple >::value, ""); - // callable_traits can facilitate the construction of std::function objects. - using bind_signature = ct::signature; - auto fn = std::function{ bind_obj }; - fn(0); - // For function objects, the following checks are determined by the // qualifiers on operator(), rather than the category of the passed value. // For member function pointers and abominable function types, the diff --git a/example/std_function/example.cpp b/example/std_function/example.cpp new file mode 100644 index 0000000..0e533e5 --- /dev/null +++ b/example/std_function/example.cpp @@ -0,0 +1,52 @@ +/* + +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) + +*/ + +#include +#undef NDEBUG +#include "make_function.hpp" + +using example::make_function; +using namespace std::placeholders; + +int add(int i, int j) { + return i + j; +} + +struct adder { + + int eval(int i, int j) const { + return i + j; + } +}; + +void check_add(std::function f) { + + auto add_result = f(99, 1); + assert(add_result == 100); +} + +int main() { + + //function pointer + auto f = make_function(&add); + check_add(f); + + //function reference + f = make_function(add); + check_add(f); + + //lambda + f = make_function([](int i, int j) { + return i + j; + }); + check_add(f); + + //member function pointer (bound to object) + f = make_function(&adder::eval, adder{}, _1, _2); + check_add(f); +} diff --git a/example/std_function/make_function.hpp b/example/std_function/make_function.hpp new file mode 100644 index 0000000..10f03cf --- /dev/null +++ b/example/std_function/make_function.hpp @@ -0,0 +1,40 @@ +#include +#include + +#ifndef EXAMPLE_ADAPTORS_HPP +#define EXAMPLE_ADAPTORS_HPP + +namespace example { + + namespace ct = callable_traits; + + //`make_function` turns any (non-overloaded) callable into std::function + template + inline decltype(auto) make_function(T&& t) { + using signature = ct::function_type; + using result_type = std::function; + return result_type{ ::std::forward(t) }; + } + + //this `make_function` overload turns a bind expression into std::function + template + inline decltype(auto) make_function(T&& t, First&& first, Others&&... others) { + + using bind_expr = decltype(::callable_traits::bind( + ::std::forward(t), + ::std::forward(first), + ::std::forward(others)... + )); + + using signature = ct::function_type; + using result_type = std::function; + + return result_type{ ::std::bind( + ::std::forward(t), + ::std::forward(first), + ::std::forward(others)... + )}; + } +} + +#endif diff --git a/example/void.cpp b/example/void.cpp index ed9eae5..26a46af 100644 --- a/example/void.cpp +++ b/example/void.cpp @@ -23,9 +23,9 @@ int main() { using expected_args = std::tuple<>; static_assert(std::is_same{}, ""); - using signature = ct::signature; + using signature = ct::function_type; using expected_signature = void(); static_assert(std::is_same{}, ""); return 0; -} \ No newline at end of file +} diff --git a/include/callable_traits/detail/bind_expression.hpp b/include/callable_traits/detail/bind_expression.hpp index 945c008..fd21d74 100644 --- a/include/callable_traits/detail/bind_expression.hpp +++ b/include/callable_traits/detail/bind_expression.hpp @@ -25,6 +25,14 @@ namespace callable_traits { namespace detail { + template + struct is_callable_traits_bind + : std::false_type {}; + + template + struct is_callable_traits_bind> + : std::true_type {}; + template struct compare_ph_value { static constexpr bool value = @@ -33,20 +41,20 @@ namespace callable_traits { template struct bind_expressions_filter; - template <> struct bind_expressions_filter<> { using type = std::tuple<>; }; + 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 + using filtered_tail = typename bind_expressions_filter::type; + using decayed_head = shallow_decay; + + using type = typename std::conditional< + is_callable_traits_bind::value, + typename prepend::type, + filtered_tail >::type; }; @@ -56,24 +64,16 @@ namespace callable_traits { }; template - struct is_empty_tuple { - using value_type = std::false_type; - }; + struct is_empty_tuple : std::false_type{}; template<> - struct is_empty_tuple> { - using value_type = std::true_type; - }; + struct is_empty_tuple> : std::true_type{}; template - struct is_not_empty_tuple { - using value_type = std::true_type; - }; + struct is_not_empty_tuple : std::true_type {}; template<> - struct is_not_empty_tuple> { - using value_type = std::false_type; - }; + struct is_not_empty_tuple> : std::false_type {}; template struct flatten_bind_expressions; @@ -91,7 +91,7 @@ namespace callable_traits { template struct flatten_bind_expressions< BindExpr, - typename is_not_empty_tuple::value_type + typename is_not_empty_tuple::type > { using type = typename prepend< BindExpr, @@ -105,16 +105,41 @@ namespace callable_traits { template struct flatten_bind_expressions< BindExpr, - typename is_empty_tuple::value_type + typename is_empty_tuple::type > { using type = std::tuple; }; + template>::value, int>::type = 0> + inline constexpr decltype(auto) unwrap_std_bind(T&& t){ + return t.get_std_bind(); + } + + template>::value, int>::type = 0> + inline constexpr T&& unwrap_std_bind(T&& t){ + return std::forward(t); + } + template struct bind_expression { + private: + + using bind_type = typename std::remove_reference(), unwrap_std_bind(std::declval())...) + )>::type; + + bind_type std_bind; + + public: + using bind_args_tuple = std::tuple< - typename categorize_bind_arg::type... + typename categorize_bind_arg< + Args, + typename std::remove_reference::type + >::type... >; using inner_bind_expressions = @@ -128,8 +153,35 @@ namespace callable_traits { bind_args_tuple >::type; - using original_args = typename traits::arg_types; + using original_args = typename traits::invoke_arg_types; using return_type = typename traits::return_type; + using result_type = return_type; + + inline constexpr bind_type& + get_std_bind() & { + return std_bind; + } + + inline constexpr bind_type&& + get_std_bind() && { + return std::move(std_bind); + } + + inline constexpr + bind_expression(Callable c, Args... args) + : std_bind( + std::bind(static_cast(c), + unwrap_std_bind(static_cast(args))...)) {} + + template + inline constexpr decltype(auto) + operator()(Rgs&&... args) { + return std_bind(std::forward(args)...); + } + + inline constexpr operator bind_type&() { + return std_bind; + } }; } diff --git a/include/callable_traits/detail/bind_expression_traits.hpp b/include/callable_traits/detail/bind_expression_traits.hpp index 3784e2c..6c25fdf 100644 --- a/include/callable_traits/detail/bind_expression_traits.hpp +++ b/include/callable_traits/detail/bind_expression_traits.hpp @@ -21,10 +21,10 @@ namespace callable_traits { static constexpr const bool value = parser::value; using traits = bind_expression_traits; using arg_types = typename parser::arg_types; + using invoke_arg_types = arg_types; using return_type = typename parser::return_type; using function_type = typename parser::function_type; using abominable_type = typename parser::abominable_type; - }; } } diff --git a/include/callable_traits/detail/categorize_bind_arg.hpp b/include/callable_traits/detail/categorize_bind_arg.hpp index 5e5fce2..f32e95d 100644 --- a/include/callable_traits/detail/categorize_bind_arg.hpp +++ b/include/callable_traits/detail/categorize_bind_arg.hpp @@ -23,34 +23,34 @@ namespace callable_traits { template struct bind_value {}; - template + template struct categorize_bind_arg { using type = typename std::conditional< - std::is_placeholder< T >::value == 0, + std::is_placeholder< NoRef >::value == 0, bind_value, - placeholder::value> + placeholder::value> >::type; }; - template - struct categorize_bind_arg< bind_value > { + template + struct categorize_bind_arg< Ref, bind_value> { using type = detail::bind_value; }; - template - struct categorize_bind_arg< std::reference_wrapper > { + template + struct categorize_bind_arg< Ref, std::reference_wrapper > { using type = std::reference_wrapper; }; - template - struct categorize_bind_arg< placeholder > { + template + struct categorize_bind_arg< Ref, placeholder > { using type = placeholder; }; - template - struct categorize_bind_arg> { + template + struct categorize_bind_arg> { - using return_type = typename bind_expression::return_type; + using return_type = typename bind_expression::return_type; using type = typename std::conditional< std::is_same::value, @@ -61,4 +61,4 @@ namespace callable_traits { } } -#endif \ No newline at end of file +#endif diff --git a/include/callable_traits/detail/function.hpp b/include/callable_traits/detail/function.hpp index 6c77f90..389ba7e 100644 --- a/include/callable_traits/detail/function.hpp +++ b/include/callable_traits/detail/function.hpp @@ -39,6 +39,7 @@ struct function using traits = function; \ using return_type = Return; \ using arg_types = std::tuple; \ + using invoke_arg_types = arg_types; \ using type = Return(Args...) QUAL; \ using function_type = Return(Args...); \ using abominable_type = Return(Args...) QUAL; \ @@ -95,6 +96,7 @@ struct function using traits = function; \ using return_type = Return; \ using arg_types = std::tuple; \ + using invoke_arg_types = arg_types; \ using type = Return (Args..., ...) QUAL; \ using function_type = Return(Args..., ...); \ using abominable_type = Return(Args..., ...) QUAL; \ diff --git a/include/callable_traits/detail/function_object.hpp b/include/callable_traits/detail/function_object.hpp index 17291a2..d678469 100644 --- a/include/callable_traits/detail/function_object.hpp +++ b/include/callable_traits/detail/function_object.hpp @@ -52,6 +52,7 @@ namespace callable_traits { using return_type = unknown; using has_varargs = std::false_type; using function_type = unknown(unknown); + using function_object_type = function_type; }; template @@ -70,7 +71,9 @@ namespace callable_traits { using type = typename General::original_type; using general_type = typename General::type; - + using function_type = typename base::function_object_type; + using invoke_arg_types = typename base::arg_types; + static constexpr const bool value = std::is_class::value && !is_integral_constant::value; diff --git a/include/callable_traits/detail/pmd.hpp b/include/callable_traits/detail/pmd.hpp index 7de6fbf..e4cb920 100644 --- a/include/callable_traits/detail/pmd.hpp +++ b/include/callable_traits/detail/pmd.hpp @@ -54,8 +54,11 @@ namespace callable_traits { using is_function_general = std::false_type; using traits = pmd; using class_type = T; - using invoke_type = T; - using base = detail::traits; + using invoke_type = T const &; + + using function_type = D(invoke_type); + using arg_types = std::tuple; + using invoke_arg_types = arg_types; using remove_member_pointer = D; diff --git a/include/callable_traits/detail/pmf.hpp b/include/callable_traits/detail/pmf.hpp index a63fba9..4cf938e 100644 --- a/include/callable_traits/detail/pmf.hpp +++ b/include/callable_traits/detail/pmf.hpp @@ -43,7 +43,9 @@ struct pmf typename std::add_lvalue_reference::type \ >::type; \ \ - using function_type = Return(Args...); \ + using function_object_type = Return(Args...); \ + using function_type = Return(invoke_type, Args...); \ + using invoke_arg_types = std::tuple; \ using abominable_type = Return(Args...) QUAL; \ using remove_varargs = type; \ using add_varargs = Return(CALLABLE_TRAITS_VARARGS_CC T::*)(Args..., ...) QUAL; \ @@ -102,7 +104,9 @@ struct pmf typename std::add_lvalue_reference::type \ >::type; \ \ - using function_type = Return(Args..., ...); \ + using function_object_type = Return(Args..., ...); \ + using function_type = Return(invoke_type, Args..., ...); \ + using invoke_arg_types = std::tuple; \ using abominable_type = Return(Args..., ...) QUAL; \ using remove_varargs = Return(T::*)(Args...) QUAL; \ using add_varargs = type; \ diff --git a/include/callable_traits/interface.hpp b/include/callable_traits/interface.hpp index ffed9a9..d158a60 100644 --- a/include/callable_traits/interface.hpp +++ b/include/callable_traits/interface.hpp @@ -24,6 +24,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include +#include #include namespace callable_traits { @@ -37,10 +38,10 @@ namespace callable_traits { using arg_at = detail::weak_at>; template - using signature = typename detail::traits::function_type; + using function_type = typename detail::traits::function_type; template - using qualified_signature = typename detail::traits::abominable_type; + using qualified_function_type = typename detail::traits::abominable_type; template using result_of = typename detail::traits::return_type; @@ -121,10 +122,10 @@ namespace callable_traits { using arg_at = detail::at>; template - using signature = detail::if_valid>; + using function_type = detail::if_valid>; template - using qualified_signature = detail::if_valid>; + using qualified_function_type = detail::if_valid>; template using result_of = detail::if_valid>; @@ -272,8 +273,8 @@ namespace callable_traits { template inline constexpr auto - bind_expr(T, Args...) -> detail::bind_expression { - return{}; + bind(T&& t, Args&&... args) -> detail::bind_expression { + return {::std::forward(t), ::std::forward(args)...}; } template diff --git a/qtcreator/callable_traits.pro b/qtcreator/callable_traits.pro index b7ecff8..c74236e 100644 --- a/qtcreator/callable_traits.pro +++ b/qtcreator/callable_traits.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS += main tests_and_examples include +SUBDIRS += main tests_and_examples include doc diff --git a/qtcreator/doc/doc.pro b/qtcreator/doc/doc.pro new file mode 100644 index 0000000..4c65dc2 --- /dev/null +++ b/qtcreator/doc/doc.pro @@ -0,0 +1,4 @@ +TEMPLATE = aux +CONFIG -= qt + +OTHER_FILES += ../../doc/* \ No newline at end of file diff --git a/qtcreator/tests_and_examples/tests_and_examples.pro b/qtcreator/tests_and_examples/tests_and_examples.pro index fa88f9a..2b2f0ca 100644 --- a/qtcreator/tests_and_examples/tests_and_examples.pro +++ b/qtcreator/tests_and_examples/tests_and_examples.pro @@ -1,14 +1,16 @@ TEMPLATE = aux CONFIG -= qt -OTHER_FILES += ../../test/*.cpp \ - ../../example/*.cpp \ - ../../example/is_constexpr/*.cpp \ - ../../example/can_invoke/*.cpp \ - ../../example/can_invoke_constexpr/*.cpp \ - ../../example/bind_expr/*.cpp \ - ../../example/args/*.cpp \ - ../../example/signature/*.cpp \ - ../../example/apply_member_pointer/*.cpp \ - ../../example/remove_member_pointer/*.cpp \ - ../../example/result_of/*.cpp \ +OTHER_FILES += ../../test/* \ + ../../test/detail/* \ + ../../example/* \ + ../../example/is_constexpr/* \ + ../../example/can_invoke/* \ + ../../example/can_invoke_constexpr/* \ + ../../example/bind/* \ + ../../example/args/* \ + ../../example/function_type/* \ + ../../example/apply_member_pointer/* \ + ../../example/remove_member_pointer/* \ + ../../example/result_of/* \ + ../../example/std_function/* \ diff --git a/test/best_match_bind.cpp b/test/best_match_bind.cpp index 6e87ae8..06c85fa 100644 --- a/test/best_match_bind.cpp +++ b/test/best_match_bind.cpp @@ -54,54 +54,38 @@ int foo(int, int, int, int) { int main() { { - auto bind_expr = ct::bind_expr( + auto b = ct::bind( &foo, - ct::bind_expr(&take_vampire, _1), - ct::bind_expr(&take_robot, _1), - ct::bind_expr(&take_dog, _1), - ct::bind_expr(&take_vampire_robot_poodle, _1) + ct::bind(&take_vampire, _1), + ct::bind(&take_robot, _1), + ct::bind(&take_dog, _1), + ct::bind(&take_vampire_robot_poodle, _1) ); - auto bind_obj = std::bind( - &foo, - std::bind(&take_vampire, _1), - std::bind(&take_robot, _1), - std::bind(&take_dog, _1), - std::bind(&take_vampire_robot_poodle, _1) - ); - - using args = ct::args; + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); VampireRobotPoodle vampire_robot_poodle; - assert(bind_obj(vampire_robot_poodle) == 0); + assert(b(vampire_robot_poodle) == 0); } { - auto bind_expr = ct::bind_expr( + auto b = ct::bind( &foo, - ct::bind_expr(&take_vampire_robot_poodle, _1), - ct::bind_expr(&take_vampire, _1), - ct::bind_expr(&take_robot, _1), - ct::bind_expr(&take_dog, _1) + ct::bind(&take_vampire_robot_poodle, _1), + ct::bind(&take_vampire, _1), + ct::bind(&take_robot, _1), + ct::bind(&take_dog, _1) ); - auto bind_obj = std::bind( - &foo, - std::bind(&take_vampire_robot_poodle, _1), - std::bind(&take_vampire, _1), - std::bind(&take_robot, _1), - std::bind(&take_dog, _1) - ); - - using args = ct::args; + using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); VampireRobotPoodle vampire_robot_poodle; - assert(bind_obj(vampire_robot_poodle) == 0); + assert(b(vampire_robot_poodle) == 0); } return 0; -} \ No newline at end of file +} diff --git a/test/bind_expression.cpp b/test/bind_expression.cpp index 4ef8961..9a71051 100644 --- a/test/bind_expression.cpp +++ b/test/bind_expression.cpp @@ -6,7 +6,6 @@ Distributed under the Boost Software License, Version 1.0. */ -#include #include #include #include @@ -20,6 +19,12 @@ Distributed under the Boost Software License, Version 1.0. #define CT_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) #endif //CT_ASSERT +#ifndef CT_RUNTIME_ASSERT +#include +#undef NDEBUG +#define CT_RUNTIME_ASSERT(...) assert(__VA_ARGS__) +#endif //CT_RUNTIME_ASSERT + using namespace std::placeholders; namespace ct = callable_traits; @@ -86,51 +91,60 @@ int main() { auto f = F{}; auto g = G{}; - assert(letters(a, b, c, d, e, f, g) == "ABCDEFG"); - assert(ordered_letters(a, b, c, d, e, f, g) == "ABCDEFG"); - + CT_RUNTIME_ASSERT(letters(a, b, c, d, e, f, g) == "ABCDEFG"); + CT_RUNTIME_ASSERT(ordered_letters(a, b, c, d, e, f, g) == "ABCDEFG"); { - auto expr = ct::bind_expr(&ordered_letters, _1, _2, _3, _4, _5, _6, _7); + auto expr = ct::bind(&ordered_letters, _1, _2, _3, _4, _5, _6, _7); + auto test = std::bind(&ordered_letters, _1, _2, _3, _4, _5, _6, _7); using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); - auto test = std::bind(&ordered_letters, _1, _2, _3, _4, _5, _6, _7); - assert(apply(test, expected_args{}) == "ABCDEFG"); + CT_ASSERT(std::is_same::value); + CT_RUNTIME_ASSERT(apply(expr, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(test, expected_args{}) == "ABCDEFG"); } { - auto expr = ct::bind_expr(&ordered_letters, a, b, c, _1, _2, _3, _4); + auto expr = ct::bind(&ordered_letters, a, b, c, _1, _2, _3, _4); + auto test = std::bind(&ordered_letters, a, b, c, _1, _2, _3, _4); using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); - auto test = std::bind(&ordered_letters, a, b, c, _1, _2, _3, _4); - assert(apply(test, expected_args{}) == "ABCDEFG"); + CT_ASSERT(std::is_same::value); + CT_RUNTIME_ASSERT(apply(test, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(test, expected_args{}) == "ABCDEFG"); } { - auto expr = ct::bind_expr(&ordered_letters, _7, _6, _5, _4, _3, _2, _1); + auto expr = ct::bind(&ordered_letters, _7, _6, _5, _4, _3, _2, _1); + auto test = std::bind(&ordered_letters, _7, _6, _5, _4, _3, _2, _1); using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); - auto test = std::bind(&ordered_letters, _7, _6, _5, _4, _3, _2, _1); - assert(apply(test, expected_args{}) == "ABCDEFG"); + CT_ASSERT(std::is_same::value); + CT_RUNTIME_ASSERT(apply(expr, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(test, expected_args{}) == "ABCDEFG"); } { - auto expr = ct::bind_expr(&ordered_letters, a, b, c, _4, _3, _2, _1); + auto expr = ct::bind(&ordered_letters, a, b, c, _4, _3, _2, _1); + auto test = std::bind(&ordered_letters, a, b, c, _4, _3, _2, _1); using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); - auto test = std::bind(&ordered_letters, a, b, c, _4, _3, _2, _1); - assert(apply(test, expected_args{}) == "ABCDEFG"); + CT_ASSERT(std::is_same::value); + CT_RUNTIME_ASSERT(apply(expr, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(test, expected_args{}) == "ABCDEFG"); } { - auto expr = ct::bind_expr(&ordered_letters, _4, _3, _2, _1, e, f, g); + auto expr = ct::bind(&ordered_letters, _4, _3, _2, _1, e, f, g); + auto test = std::bind(&ordered_letters, _4, _3, _2, _1, e, f, g); using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); - auto test = std::bind(&ordered_letters, _4, _3, _2, _1, e, f, g); - assert(apply(test, expected_args{}) == "ABCDEFG"); + CT_ASSERT(std::is_same::value); + CT_RUNTIME_ASSERT(apply(expr, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(test, expected_args{}) == "ABCDEFG"); } { - auto expr = ct::bind_expr(&letters, _1, _1, _3, _3, _2, a, b); + auto expr = ct::bind(&letters, _1, _1, _3, _3, _2, a, b); using args = ct::args; using expected_args = std::tuple; CT_ASSERT(std::is_same::value); } return 0; -} \ No newline at end of file +} diff --git a/test/bind_expression_parser.cpp b/test/bind_expression_parser.cpp index 514b215..825b32d 100644 --- a/test/bind_expression_parser.cpp +++ b/test/bind_expression_parser.cpp @@ -6,7 +6,6 @@ Distributed under the Boost Software License, Version 1.0. */ -#include #include #include #include @@ -20,6 +19,12 @@ Distributed under the Boost Software License, Version 1.0. #define CT_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) #endif //CT_ASSERT +#ifndef CT_RUNTIME_ASSERT +#include +#undef NDEBUG +#define CT_RUNTIME_ASSERT(...) assert(__VA_ARGS__) +#endif //CT_RUNTIME_ASSERT + using namespace std::placeholders; namespace ct = callable_traits; @@ -40,7 +45,7 @@ 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 +// with ct::bind auto ordered_letters(A a, B b, C c, D d, E e, F f, G g) { std::stringstream ss{}; @@ -88,7 +93,7 @@ const auto f = F{}; const auto g = G{}; // lets us create a complex bind expression with both -// `std::bind` and `ct::bind_expr` +// `std::bind` and `ct::bind` #define BIND_WITH(bind_name) \ bind_name(&ordered_letters, \ _1, \ @@ -123,43 +128,16 @@ int main() { assert(ordered_letters(a, b, c, d, e, f, g) == "ABCDEFG"); - using bind_expr = decltype(BIND_WITH(ct::bind_expr)); + auto ct_bind = BIND_WITH(ct::bind); + auto std_bind = BIND_WITH(std::bind); - check_expression_flattening(); - - using args = ct::args; - - // these are the argument types as dictated by - // the bind expression's placeholders + // these are the argument types as dictated by the bind expression's placeholders using expected_args = std::tuple; + using args = ct::args; CT_ASSERT(std::is_same::value); - - auto runtime_test = BIND_WITH(std::bind); - assert(apply(runtime_test, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(ct_bind, expected_args{}) == "ABCDEFG"); + CT_RUNTIME_ASSERT(apply(std_bind, 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{}); -} diff --git a/test/detail/flatten_bind_expressions.cpp b/test/detail/flatten_bind_expressions.cpp new file mode 100644 index 0000000..cdd05a5 --- /dev/null +++ b/test/detail/flatten_bind_expressions.cpp @@ -0,0 +1,73 @@ +/* + +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 + +#ifndef CT_ASSERT +#define CT_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) +#endif //CT_ASSERT + +using namespace std::placeholders; +namespace ct = callable_traits; + +struct A {}; +struct B {}; +struct C {}; +struct D {}; +struct E {}; +struct F {}; +struct 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 + +auto ordered_letters(A, B, C, D, E, F, G) { + return 0; +} + +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{}; +} + +#define INNER_3 ct::bind(&BEEF_returns_B, B{}, _10, E{}, F{}) +#define INNER_2 ct::bind(&BEEF_returns_G, INNER_3, _9, E{}, _8) +#define INNER_1 ct::bind(&BEEF_returns_D, _2, E{}, _4, _7) + +int main() { + + auto root = ct::bind(&ordered_letters, _1, _2, _3, INNER_1, _5, _6, INNER_2); + + using test = decltype(root)::flattened_bind_expressions; + + using expect = std::tuple< + decltype(root), + decltype(INNER_1), + decltype(INNER_2), + decltype(INNER_3) + >; + + CT_ASSERT(std::is_same::value); + + return 0; +} diff --git a/test/varargs.cpp b/test/varargs.cpp index 2c2ac45..15f18d0 100644 --- a/test/varargs.cpp +++ b/test/varargs.cpp @@ -61,9 +61,9 @@ int main() { CT_ASSERT(decltype(ct::has_varargs(&foo6)){}); CT_ASSERT(decltype(ct::has_varargs()){}); } { - //qualified_signature removes the member pointer - using f1 = ct::qualified_signature; - using f2 = ct::qualified_signature; + //qualified_function_type removes the member pointer + using f1 = ct::qualified_function_type; + using f2 = ct::qualified_function_type; CT_ASSERT(std::is_same, f2>{}); CT_ASSERT(std::is_same>{}); } {