diff --git a/example/can_invoke/member_function_pointer.cpp b/example/can_invoke/member_function_pointer.cpp new file mode 100644 index 0000000..a607092 --- /dev/null +++ b/example/can_invoke/member_function_pointer.cpp @@ -0,0 +1,32 @@ +/*! +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) +*/ + +#include +#include + +// NOTE: Due to non-compliance in MSVC, can_invoke_constexpr +// always returns std::false_type on that compiler, which +// causes a static assert below to fail. + +namespace ct = callable_traits; + +struct foo { + int bar(int) const { + return 1; + } +}; + +// can_invoke returns std::true_type here because the +// arguments are valid to INVOKE +static_assert(ct::can_invoke(&foo::bar, foo{}, 0), ""); + + +// can_invoke returns std::false_type here because the +// arguments are NOT valid to INVOKE +static_assert(!ct::can_invoke_constexpr(&foo::bar, foo{}), ""); + +int main() { return 0; } diff --git a/include/callable_traits/config.hpp b/include/callable_traits/config.hpp index 8131fba..f884388 100644 --- a/include/callable_traits/config.hpp +++ b/include/callable_traits/config.hpp @@ -28,8 +28,8 @@ Distributed under the Boost Software License, Version 1.0. #endif //_MSC_VER -#ifndef CALLABLE_TRAITS_MAX_ARITY_SEARCH -#define CALLABLE_TRAITS_MAX_ARITY_SEARCH 10 -#endif //CALLABLE_TRAITS_MAX_ARITY_SEARCH +#ifndef CALLABLE_TRAITS_ARITY_SEARCH_LIMIT +#define CALLABLE_TRAITS_ARITY_SEARCH_LIMIT 10 +#endif //CALLABLE_TRAITS_ARITY_SEARCH_LIMIT #endif diff --git a/include/callable_traits/detail/arity.hpp b/include/callable_traits/detail/arity.hpp index 5b64d9c..a78fe03 100644 --- a/include/callable_traits/detail/arity.hpp +++ b/include/callable_traits/detail/arity.hpp @@ -87,10 +87,6 @@ namespace callable_traits { static constexpr bool value = result_type::value; }; - struct dummy_wrapper { - using arg_types = std::tuple; - }; - template struct arg_tuple_size { static constexpr int value = -1; diff --git a/include/callable_traits/detail/can_dereference.hpp b/include/callable_traits/detail/can_dereference.hpp deleted file mode 100644 index c47ed0d..0000000 --- a/include/callable_traits/detail/can_dereference.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/*! -@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_DETAIL_CAN_DEREFERENCE_HPP -#define CALLABLE_TRAITS_DETAIL_CAN_DEREFERENCE_HPP - -#include -#include - -namespace callable_traits { - - namespace detail { - - template - struct can_dereference_t - { - template - struct check {}; - - template - static std::int8_t test( - check())>::type>* - ); - - template - static std::int16_t test(...); - - static constexpr const bool value = - sizeof(test(nullptr)) == sizeof(std::int8_t); - }; - - template - using can_dereference = std::integral_constant::value - >; - } -} - -#endif \ No newline at end of file diff --git a/include/callable_traits/detail/can_invoke_constexpr_impl.hpp b/include/callable_traits/detail/can_invoke_constexpr_impl.hpp index fe95304..5cb91cc 100644 --- a/include/callable_traits/detail/can_invoke_constexpr_impl.hpp +++ b/include/callable_traits/detail/can_invoke_constexpr_impl.hpp @@ -6,35 +6,104 @@ #include #include +#include +#include namespace callable_traits { namespace detail { - template, - is_constexpr_constructible... - >::value, int>::type = 0> +#ifdef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED + + inline constexpr auto + can_invoke_constexpr_impl(...) { + return std::false_type{}; + } + +#else + + template + struct test_invoke_constexpr { + auto operator()(...) const -> substitution_failure; + }; + + template + struct test_invoke_constexpr, T, Args...> { + + using class_t = typename pmf::class_type; + + // An arbitrary expression as the left-hand argument to a non-overloaded + // comma operator expression that is passed as a template value argument + // will trigger a substitution failure when the expression is not constexpr + template> + auto operator()(P&& p, U&& u, Rgs&&... rgs) const -> if_integral_constant, CALLABLE_TRAITS_MAKE_CONSTEXPR(U&&) + // resolves to a matching reference to a constexpr K object. Hence, K must be + // a literal type with a constexpr default constructor. any_arg_evaluated + // is a "chameleon" type that tries to pass as anything. Generally, if K is + // templated and uses dependent names, this will fail. However, There are a few + // exceptions: Unary/binary operators, value member `value`, and member alias + // `type` are all defined in terms of any_arg_evaluated, so usage of these + // will succeed in K. + + + ((CALLABLE_TRAITS_MAKE_CONSTEXPR(Obj).*std::remove_reference

::type::value)( + CALLABLE_TRAITS_MAKE_CONSTEXPR(Rgs&&)... + ), 0)>>; + + auto operator()(...) const -> substitution_failure; + }; + + template + struct test_invoke_constexpr, Args...> { + auto operator()(...) const -> substitution_failure; + }; + + template + struct test_invoke_constexpr { + + //see comments on specialization above + template + auto operator()(T&& t, Rgs&&...) const -> if_not_integral_constant>; + + template::type> + auto operator()(T&& t, Rgs&&...) const -> if_integral_constant>; + + auto operator()(...) const -> substitution_failure; + }; + + template + using are_all_constexpr_constructible = conjunction< + is_constexpr_constructible... + >; + + template>::value, int>::type = 0> inline constexpr auto can_invoke_constexpr_impl(T&&, Args&&...) { return std::false_type{}; } - template, - is_constexpr_constructible... - >::value, int>::type = 0> + template::value, int>::type = 0> inline constexpr auto can_invoke_constexpr_impl(T&& t, Args&&... args) { - using traits = detail::traits; - using test = detail::test_invoke_constexpr; + using traits = traits; + using test = test_invoke_constexpr; using result = decltype(test{}(::std::forward(t), ::std::forward(args)...)); - using failure = detail::substitution_failure; + using failure = substitution_failure; using is_invalid_invoke = std::is_same; return std::integral_constant{}; } + +#endif //ifndef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED } } #endif // CALLABLE_TRAITS_DETAIL_CAN_INVOKE_CONSTEXPR_T_HPP diff --git a/include/callable_traits/detail/generalize.hpp b/include/callable_traits/detail/generalize.hpp deleted file mode 100644 index a78b1cd..0000000 --- a/include/callable_traits/detail/generalize.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/*! -@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_DETAIL_GENERALIZE_HPP -#define CALLABLE_TRAITS_DETAIL_GENERALIZE_HPP - -#include -#include -#include - -namespace callable_traits { - - namespace detail { - - template - struct generalize_t { - using type = T; - }; - - template - struct generalize_t::value && !is_reference_wrapper::value - >>{ - using type = decltype(*std::declval()); - }; - - template - struct generalize_t> { - using type = decltype(std::declval().get()); - }; - - // generalize expects a pointer, a reference, or a reference_wrapper. - // When T is a pointer, generalize is the resulting type - // of the pointer dereferenced. When T is an std::reference_wrapper, - // generalize is the underlying reference type. Otherwise, generalize - // is T. - - template - using generalize = typename generalize_t::type; - } -} - -#endif diff --git a/include/callable_traits/detail/generalized_class.hpp b/include/callable_traits/detail/generalized_class.hpp index fca6787..1feeefe 100644 --- a/include/callable_traits/detail/generalized_class.hpp +++ b/include/callable_traits/detail/generalized_class.hpp @@ -10,7 +10,7 @@ Distributed under the Boost Software License, Version 1.0. #ifndef CALLABLE_TRAITS_DETAIL_GENERALIZED_CLASS_HPP #define CALLABLE_TRAITS_DETAIL_GENERALIZED_CLASS_HPP -#include +#include #include #include @@ -67,4 +67,4 @@ namespace callable_traits { } } -#endif \ No newline at end of file +#endif diff --git a/include/callable_traits/detail/is_constexpr_impl.hpp b/include/callable_traits/detail/is_constexpr_impl.hpp index 689f609..b3b9611 100644 --- a/include/callable_traits/detail/is_constexpr_impl.hpp +++ b/include/callable_traits/detail/is_constexpr_impl.hpp @@ -1,5 +1,13 @@ -#ifndef CAN_INVOKE_IMPL_HPP -#define CAN_INVOKE_IMPL_HPP +/*! +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_IS_CONSTEXPR_IMPL_HPP +#define CALLABLE_TRAITS_IS_CONSTEXPR_IMPL_HPP #include #include @@ -18,7 +26,14 @@ namespace callable_traits { namespace detail { -#ifndef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED +#ifdef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED + + inline constexpr auto + is_constexpr_impl(...) { + return std::false_type{}; + } + +#else template struct is_constexpr_t { @@ -28,10 +43,29 @@ namespace callable_traits { template struct is_constexpr_t> { + // An arbitrary expression as the left-hand argument to a non-overloaded + // comma operator expression that is passed as a template value argument + // will trigger a substitution failure when the expression is not constexpr template auto operator()(T&& t) const -> if_not_integral_constant{}...), 0)>>; + + // Where U = std::remove_reference_t, CALLABLE_TRAITS_MAKE_CONSTEXPR(T&&) + // resolves to a matching reference to a constexpr U object. Hence, U must be + // a literal type with a constexpr default constructor. any_arg_evaluated + // is a "chameleon" type that tries to pass as anything. Generally, if U is + // templated and uses dependent names, this will fail. However, There are a few + // exceptions: Unary/binary operators, value member `value`, and member alias + // `type` are all defined in terms of any_arg_evaluated, so usage of these + // will succeed in U. + + ( + CALLABLE_TRAITS_MAKE_CONSTEXPR(T&&)( + ::callable_traits::detail::any_arg_evaluated{}... + ), + 0 + ) + >>; template::type> auto operator()(T&& t) const -> if_integral_constant struct is_constexpr_t, std::index_sequence> { - using class_t = typename pmf::invoke_type; + // When `Pmf` is `int(foo::*)() const &&`, invoke_type is `foo const &&` + using invoke_type = typename pmf::invoke_type; + //see comments on specialization above template auto operator()(P&&) const -> if_integral_constant::type::value)( + ((CALLABLE_TRAITS_MAKE_CONSTEXPR(invoke_type).* ::std::remove_reference

::type::value)( ::callable_traits::detail::any_arg_evaluated{}... ), 0)>>; auto operator()(...) const -> substitution_failure; }; + // Since constexpr data members must be static, you cannot create PMDs to them template struct is_constexpr_t, std::index_sequence> { auto operator()(...) const -> substitution_failure; @@ -79,15 +116,10 @@ namespace callable_traits { return std::integral_constant{}; } -#else - inline constexpr auto - is_constexpr_impl(...) { - return std::false_type{}; - } +#endif //ifdef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED -#endif //ifndef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED } } -#endif // CAN_INVOKE_IMPL_HPP +#endif // CALLABLE_TRAITS_IS_CONSTEXPR_IMPL_HPP diff --git a/include/callable_traits/detail/private_tuple.hpp b/include/callable_traits/detail/private_tuple.hpp new file mode 100644 index 0000000..c0ec30a --- /dev/null +++ b/include/callable_traits/detail/private_tuple.hpp @@ -0,0 +1,23 @@ +/*! +@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_DETAIL_PRIVATE_TUPLE_HPP +#define CALLABLE_TRAITS_DETAIL_PRIVATE_TUPLE_HPP + +namespace callable_traits { + + namespace detail { + + template + struct private_tuple { + using to_std_tuple = std::tuple; + }; + + } +} + +#endif \ No newline at end of file diff --git a/include/callable_traits/detail/test_invoke.hpp b/include/callable_traits/detail/test_invoke.hpp index d6d7b80..319735b 100644 --- a/include/callable_traits/detail/test_invoke.hpp +++ b/include/callable_traits/detail/test_invoke.hpp @@ -10,8 +10,6 @@ Distributed under the Boost Software License, Version 1.0. #ifndef CALLABLE_TRAITS_DETAIL_TEST_INVOKE_HPP #define CALLABLE_TRAITS_DETAIL_TEST_INVOKE_HPP -#include -#include #include #include #include @@ -26,13 +24,6 @@ namespace callable_traits { template struct success {}; - template>, - typename IsSame = std::is_same>> - using generalize_if_dissimilar = typename std::conditional< - IsBaseOf::value || IsSame::value, T, generalize - >::type; - template struct test_invoke; @@ -144,51 +135,6 @@ namespace callable_traits { template using is_invokable = build_invoke_t< typename Traits::is_member_pointer, Traits, Args...>; - - template - struct test_invoke_constexpr { - auto operator()(...) const->substitution_failure; - }; - -#ifndef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED - - template - struct test_invoke_constexpr, T, Args...> { - - using class_t = typename pmf::class_type; - - template> - auto operator()(P&& p, U&& u, Rgs&&... rgs) const -> if_integral_constant::type::value)( - CALLABLE_TRAITS_MAKE_CONSTEXPR(Rgs&&)... - ), 0)>>; - - auto operator()(...) const -> substitution_failure; - }; - - template - struct test_invoke_constexpr, Args...> { - auto operator()(...) const -> substitution_failure; - }; - - template - struct test_invoke_constexpr { - - template - auto operator()(T&& t, Rgs&&...) const -> if_not_integral_constant>; - - template::type> - auto operator()(T&& t, Rgs&&...) const -> if_integral_constant>; - - auto operator()(...) const -> substitution_failure; - }; - -#endif //ifndef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED } } diff --git a/include/callable_traits/detail/utility.hpp b/include/callable_traits/detail/utility.hpp index 472546a..f509d52 100644 --- a/include/callable_traits/detail/utility.hpp +++ b/include/callable_traits/detail/utility.hpp @@ -14,11 +14,12 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include namespace callable_traits { struct constants { - static constexpr std::size_t arity_search_limit = CALLABLE_TRAITS_MAX_ARITY_SEARCH; + static constexpr std::size_t arity_search_limit = CALLABLE_TRAITS_ARITY_SEARCH_LIMIT; }; struct invalid_type { invalid_type() = delete; }; @@ -28,37 +29,45 @@ struct unknown { unknown() = delete; }; namespace detail { + // used to convey "this type doesn't matter" in code struct dummy {}; + + // used as return type in failed SFINAE tests struct substitution_failure{}; + + // shorthand for std::tuple_element template using at = typename std::tuple_element::type; - template - struct weak_at_t { - using type = at; - }; + namespace util_detail { + + template + struct weak_at_t { + using type = at; + }; + + template + struct weak_at_t= std::tuple_size::value>>{ + using type = invalid_type; + }; + } + + // bounds-checked version of at (see above) template - struct weak_at_t= std::tuple_size::value>>{ - using type = invalid_type; - }; + using weak_at = typename util_detail::weak_at_t::type; - template - using weak_at = typename weak_at_t::type; - - template - struct value_type_pair { - using type = T; - static constexpr const bool value = Value; - }; + // a faster version of std::decay_t template using shallow_decay = typename std::remove_cv< typename std::remove_reference::type >::type; + + //polyfill for C++17 std::conjunction template struct conjunction : std::true_type {}; @@ -71,6 +80,8 @@ struct unknown { unknown() = delete; }; struct conjunction : std::conditional>::type { }; + + //polyfill for C++17 std::disjunction template struct disjunction : std::false_type {}; @@ -83,21 +94,29 @@ struct unknown { unknown() = delete; }; struct disjunction : std::conditional>::type { }; + + //polyfill for C++17 negation template using negate = std::integral_constant; - template - struct is_reference_wrapper_t { - using type = std::false_type; - }; + + namespace util_detail { + + template + struct is_reference_wrapper_t { + using type = std::false_type; + }; + + template + struct is_reference_wrapper_t> { + using type = std::true_type; + }; + } template - struct is_reference_wrapper_t> { - using type = std::true_type; - }; + using is_reference_wrapper = + typename util_detail::is_reference_wrapper_t>::type; - template - using is_reference_wrapper = typename is_reference_wrapper_t>::type; template struct unwrap_reference_t { @@ -109,22 +128,28 @@ struct unknown { unknown() = delete; }; using type = decltype(std::declval().get()); }; + // removes std::reference_wrapper template using unwrap_reference = typename unwrap_reference_t::type; - template - struct is_integral_constant_t { - static constexpr bool value = false; - }; - template - struct is_integral_constant_t> { - static constexpr bool value = true; - }; + namespace util_detail { + + template + struct is_integral_constant_t { + using type = std::false_type; + }; + + template + struct is_integral_constant_t> { + using type = std::true_type; + }; + } template - using is_integral_constant = std::integral_constant>::value>; + using is_integral_constant = typename util_detail::is_integral_constant_t< + shallow_decay>::type; + template using if_integral_constant = @@ -134,39 +159,116 @@ struct unknown { unknown() = delete; }; using if_not_integral_constant = typename std::enable_if::value, Result>::type; + template using add_member_pointer = T Class::*; - template - struct remove_member_pointer_t { - using type = T; - }; - template - struct remove_member_pointer_t{ - using type = T; - }; + namespace util_detail { + + template + struct remove_member_pointer_t { + using type = T; + }; + + template + struct remove_member_pointer_t{ + using type = T; + }; + } template using remove_member_pointer = - typename remove_member_pointer_t::type; + typename util_detail::remove_member_pointer_t::type; + template> using is_constexpr_constructible = std::integral_constant::value && std::is_default_constructible::value >; - template - struct build_function_t; - template - struct build_function_t>{ - using type = Return(Args...); - }; + namespace util_detail { + + template + struct build_function_t; + + template + struct build_function_t>{ + using type = Return(Args...); + }; + } template - using build_function = typename build_function_t::type; + using build_function = + typename util_detail::build_function_t::type; + + namespace util_detail { + + template + struct can_dereference_t + { + template + struct check {}; + + template + static std::int8_t test( + check())>::type>* + ); + + template + static std::int16_t test(...); + + static constexpr const bool value = + sizeof(test(nullptr)) == sizeof(std::int8_t); + }; + } + + //returns std::true_type for pointers and smart pointers + template + using can_dereference = std::integral_constant::value + >; + + + namespace util_detail { + + template + struct generalize_t { + using type = T; + }; + + template + struct generalize_t::value && !is_reference_wrapper::value + >>{ + using type = decltype(*std::declval()); + }; + + template + struct generalize_t> { + using type = decltype(std::declval().get()); + }; + } + + // When T is a pointer, generalize is the resulting type of the + // pointer dereferenced. When T is an std::reference_wrapper, generalize + // is the underlying reference type. Otherwise, generalize is T. + template + using generalize = typename util_detail::generalize_t::type; + + + // handles the member pointer rules of INVOKE + template>, + typename IsSame = std::is_same>> + using generalize_if_dissimilar = typename std::conditional< + IsBaseOf::value || IsSame::value, T, generalize + >::type; + + + //used to prepend a type to a tuple template struct prepend; template <> struct prepend<> { diff --git a/qtcreator/main/main.cpp b/qtcreator/main/main.cpp index d8cdef0..a607092 100644 --- a/qtcreator/main/main.cpp +++ b/qtcreator/main/main.cpp @@ -1,4 +1,3 @@ - /*! Copyright (c) 2016 Barrett Adair @@ -9,28 +8,25 @@ Distributed under the Boost Software License, Version 1.0. #include #include -// NOTE: Due to non-compliance in MSVC, is_constexpr always -// returns std::false_type on that compiler, which causes -// the static asserts below to fail. +// NOTE: Due to non-compliance in MSVC, can_invoke_constexpr +// always returns std::false_type on that compiler, which +// causes a static assert below to fail. namespace ct = callable_traits; -constexpr int foo(const int&) { - return 1; -} +struct foo { + int bar(int) const { + return 1; + } +}; -int bar(const int&) { - return 1; -} +// can_invoke returns std::true_type here because the +// arguments are valid to INVOKE +static_assert(ct::can_invoke(&foo::bar, foo{}, 0), ""); -// for is_constexpr calls, function pointers must -// be passed as std::integral_constants -using F = std::integral_constant; -using B = std::integral_constant; -static_assert(ct::is_constexpr(F{}), ""); -static_assert(ct::is_constexpr(), ""); -static_assert(!ct::is_constexpr(B{}), ""); -static_assert(!ct::is_constexpr(), ""); +// can_invoke returns std::false_type here because the +// arguments are NOT valid to INVOKE +static_assert(!ct::can_invoke_constexpr(&foo::bar, foo{}), ""); int main() { return 0; }