diff --git a/CMakeLists.txt b/CMakeLists.txt index a7f532c..ddd9169 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ list(APPEND closure_src closure/placeholders.hpp closure/util.hpp closure/bind.hpp + closure/trivial_tuple.hpp ) add_executable(closure_test diff --git a/README.md b/README.md index bd97173..f8e8491 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![macOS Status](https://github.com/coyorkdow/closure/actions/workflows/macos.yml/badge.svg)](https://github.com/coyorkdow/closure/actions/workflows/macos.yml) [![C++ Standard](https://img.shields.io/badge/cpp14-minimum%20required-success?logo=cplusplus)](https://en.cppreference.com/w/cpp/14) -Closure is header-only. To use Closure, simply copy `closure.hpp` and dicectory `closure` into your project. +Closure is header-only. To use Closure, simply copy `closure.hpp` and dicectory `closure` into your project, and don't forget `#include "closure.hpp"`. ## Features @@ -214,6 +214,6 @@ Since `closure::Any` means any type, you can assign `c` to any other closure wit ```C++ // it's ok that the 4th parameter is float type, because float can implicitly convert to int Closure c2(lambda, PlaceHolder<1>(), PlaceHolder<3>()); -c2 = closure; +c2 = c; c2(1, 2, "3", 4.2); // result is 6 ``` diff --git a/closure.hpp b/closure.hpp index e774c3c..422930f 100644 --- a/closure.hpp +++ b/closure.hpp @@ -12,6 +12,7 @@ #include "closure/bind.hpp" #include "closure/placeholders.hpp" #include "closure/traits.hpp" +#include "closure/trivial_tuple.hpp" namespace closure { @@ -96,7 +97,7 @@ template struct MaybeStores { public: static constexpr bool value = placeholders::HasNonGetter>::value; - using type = std::conditional_t()...)), std::tuple<>>; + using type = std::conditional_t...>, tuple::TrivialTuple<>>; }; // Overload for function pointer and functor @@ -117,7 +118,7 @@ class ClosureImpl> : public Closure public: using callee_type = Callable; - using stored_types = decltype(std::make_tuple(std::declval()...)); /*std::tuple*/ + using stored_types = tuple::TrivialTuple...>; using maybe_stores = MaybeStores; static_assert(!maybe_stores::value || std::is_same::value, "maybe stored type is not same as the stored_types"); @@ -176,7 +177,7 @@ class ClosureImpl> : public Closure template = 0, std::size_t... I> typename base::result_type InvokeHelper(std::index_sequence, Args&&... args) { - return invoke::INVOKE(callable_, std::get(static_cast(*this))..., std::forward(args)...); + return invoke::INVOKE(callable_, tuple::get(static_cast(*this))..., std::forward(args)...); } callee_type callable_; @@ -464,25 +465,33 @@ class Closure { }; // MakeClosure for function pointer, remove cv-qualifier and noexcept + template >::value, int> = 0> -auto MakeClosure(R (*fptr)(Args...), Bounds&&... bound_args) { +auto MakeClosure_ClosureImplType(R (*fptr)(Args...), Bounds&&... bound_args) { using closure_args = closureimpl::RemovePrefixWeakT, ArgList>; - using closure_res_t = typename std::remove_pointer_t( - nullptr, closure_args{}, fptr, std::forward(bound_args)...))>::closure_type; - return Closure(fptr, std::forward(bound_args)...); + using type = + decltype(closureimpl::MakeClosureImpl(nullptr, closure_args{}, fptr, std::forward(bound_args)...)); + return (type)(nullptr); } template >::value, int> = 0> -auto MakeClosure(R (*fptr)(Args...), Bounds&&... bound_args) { +auto MakeClosure_ClosureImplType(R (*fptr)(Args...), Bounds&&... bound_args) { using bounds_l = ArgList; using args_l = ArgList; using replaced_types = closureimpl::ReplacePlaceHoldersWithGettersT; using closure_args = ConcatT::agents_prototype, closureimpl::RemovePrefixWeakT>; - using closure_res_t = typename std::remove_pointer_t( - nullptr, closure_args{}, replaced_types{}, fptr, std::forward(bound_args)...))>::closure_type; + using type = decltype(closureimpl::MakeClosureImpl(nullptr, closure_args{}, replaced_types{}, fptr, + std::forward(bound_args)...)); + return (type)(nullptr); +} + +template +auto MakeClosure(R (*fptr)(Args...), Bounds&&... bound_args) { + using impl_type = decltype(MakeClosure_ClosureImplType(fptr, std::forward(bound_args)...)); + using closure_res_t = typename std::remove_pointer_t::closure_type; return Closure(fptr, std::forward(bound_args)...); } @@ -494,30 +503,36 @@ template >::value && !placeholders::HasPlaceHolder>::value, int> = 0> -auto MakeClosure(Functor&& functor, Bounds&&... bound_args) { +auto MakeClosure_ClosureImplType(Functor&& functor, Bounds&&... bound_args) { using functor_traits = traits::SimpleFunctorTraits>; - using closure_res_t = - typename std::remove_pointer_t( - nullptr, typename functor_traits::args_type{}, std::forward(functor), - std::forward(bound_args)...))>::closure_type; - return Closure(std::forward(functor), std::forward(bound_args)...); + using type = decltype(closureimpl::MakeClosureImpl( + nullptr, typename functor_traits::args_type{}, std::forward(functor), + std::forward(bound_args)...)); + return (type)(nullptr); } template >::value && placeholders::HasPlaceHolder>::value, int> = 0> -auto MakeClosure(Functor&& functor, Bounds&&... bound_args) { +auto MakeClosure_ClosureImplType(Functor&& functor, Bounds&&... bound_args) { using functor_traits = traits::SimpleFunctorTraits>; using bounds_l = ArgList; using args_l = typename functor_traits::args_type; using replaced_types = closureimpl::ReplacePlaceHoldersWithGettersT; using closure_args = ConcatT::agents_prototype, closureimpl::RemovePrefixWeakT>; - using closure_res_t = - typename std::remove_pointer_t( - nullptr, closure_args{}, replaced_types{}, std::forward(functor), - std::forward(bound_args)...))>::closure_type; + using type = decltype(closureimpl::MakeClosureImpl( + nullptr, closure_args{}, replaced_types{}, std::forward(functor), std::forward(bound_args)...)); + return (type)(nullptr); +} + +template >::value, int> = 0> +auto MakeClosure(Functor&& functor, Bounds&&... bound_args) { + using impl_type = + decltype(MakeClosure_ClosureImplType(std::forward(functor), std::forward(bound_args)...)); + using closure_res_t = typename std::remove_pointer_t::closure_type; return Closure(std::forward(functor), std::forward(bound_args)...); } @@ -526,22 +541,22 @@ template ::value && !placeholders::HasPlaceHolder>::value, int> = 0> -auto MakeClosure(Method method, Bounds&&... bound_args) { +auto MakeClosure_ClosureImplType(Method method, Bounds&&... bound_args) { using mfp_traits = traits::MemberFunctionPointerTraits; using class_type = typename mfp_traits::class_type; using args_type = typename mfp_traits::args_type; using return_type = typename mfp_traits::return_type; using closure_args = closureimpl::RemovePrefixWeakT, ConcatT, args_type>>; - using closure_res_t = typename std::remove_pointer_t( - nullptr, closure_args{}, method, std::forward(bound_args)...))>::closure_type; - return Closure(method, std::forward(bound_args)...); + using type = decltype(closureimpl::MakeClosureImpl(nullptr, closure_args{}, method, + std::forward(bound_args)...)); + return (type)(nullptr); } template ::value && placeholders::HasPlaceHolder>::value, int> = 0> -auto MakeClosure(Method method, Bounds&&... bound_args) { +auto MakeClosure_ClosureImplType(Method method, Bounds&&... bound_args) { using mfp_traits = traits::MemberFunctionPointerTraits; using bounds_l = ArgList; using class_type = typename mfp_traits::class_type; @@ -550,8 +565,15 @@ auto MakeClosure(Method method, Bounds&&... bound_args) { using replaced_types = closureimpl::ReplacePlaceHoldersWithGettersT; using closure_args = ConcatT::agents_prototype, closureimpl::RemovePrefixWeakT>; - using closure_res_t = typename std::remove_pointer_t( - nullptr, closure_args{}, replaced_types{}, method, std::forward(bound_args)...))>::closure_type; + using type = decltype(closureimpl::MakeClosureImpl(nullptr, closure_args{}, replaced_types{}, method, + std::forward(bound_args)...)); + return type(nullptr); +} + +template ::value, int> = 0> +auto MakeClosure(Method method, Bounds&&... bound_args) { + using impl_type = decltype(MakeClosure_ClosureImplType(method, std::forward(bound_args)...)); + using closure_res_t = typename std::remove_pointer_t::closure_type; return Closure(method, std::forward(bound_args)...); } diff --git a/closure/placeholders.hpp b/closure/placeholders.hpp index cde6722..08e9e9f 100644 --- a/closure/placeholders.hpp +++ b/closure/placeholders.hpp @@ -5,6 +5,7 @@ #pragma once #include "closure/traits.hpp" +#include "closure/trivial_tuple.hpp" #include "closure/util.hpp" namespace closure { @@ -151,15 +152,15 @@ template struct HasNonGetter, Os...>> : HasNonGetter> {}; template (std::declval()))>>::value, int> = 0> + std::enable_if_t(std::declval()))>>::value, int> = 0> decltype(auto) TryMapAndGet(Tuple&& tuple, Agents&& agents) noexcept { - return std::get(std::forward(tuple)).Get(agents); + return tuple::get(std::forward(tuple)).Get(agents); } template (std::declval()))>>::value, int> = 0> + std::enable_if_t(std::declval()))>>::value, int> = 0> decltype(auto) TryMapAndGet(Tuple&& tuple, Agents&&) noexcept { - return std::get(std::forward(tuple)); + return tuple::get(std::forward(tuple)); } }; // namespace placeholders diff --git a/closure/traits.hpp b/closure/traits.hpp index bf1a018..a65031e 100644 --- a/closure/traits.hpp +++ b/closure/traits.hpp @@ -100,55 +100,6 @@ using std::void_t; namespace traits { -template -struct IsFunctionOrPointerToFunction - : std::integral_constant::value || std::is_function>::value> { -}; - -template ), decltype(&Tp::operator*)>* = nullptr> -auto IsDereferencableImpl(int) -> std::integral_constant< - bool, std::is_pointer().operator->())>::value && - std::is_reference().operator*())>::value && - std::is_same().operator->())>, - std::remove_reference_t().operator*())>>::value>; - -template -auto IsDereferencableImpl(...) -> std::is_pointer; - -template -struct IsDereferencable : decltype(IsDereferencableImpl(0)) {}; - -template -auto TryCallMethod(int, Dereferencable&&, Method&&, Args&&...) - -> decltype(((*std::declval()).*std::declval())(std::declval()...), std::true_type{}); - -auto TryCallMethod(float, ...) -> std::false_type; - -template -struct CanUsePointerToMemberFunction : std::false_type {}; - -template -struct CanUsePointerToMemberFunction - : decltype(TryCallMethod(0, std::declval(), std::declval(), - std::declval()...)) {}; - -template -struct CanUsePointerToMemberFunction - : decltype(TryCallMethod(0, std::declval(), std::declval(), - std::declval()...)) {}; - -#if !(__cplusplus < 201703L) -template -struct CanUsePointerToMemberFunction - : decltype(TryCallMethod(0, std::declval(), std::declval(), - std::declval()...)) {}; - -template -struct CanUsePointerToMemberFunction - : decltype(TryCallMethod(0, std::declval(), std::declval(), - std::declval()...)) {}; -#endif - template struct MemberFunctionPointerTraits {}; @@ -166,6 +117,20 @@ struct MemberFunctionPointerTraits { using args_type = ArgList; }; +template +struct MemberFunctionPointerTraits { + using class_type = Class; + using return_type = R; + using args_type = ArgList; +}; + +template +struct MemberFunctionPointerTraits { + using class_type = Class; + using return_type = R; + using args_type = ArgList; +}; + #if !(__cplusplus < 201703L) template struct MemberFunctionPointerTraits { @@ -180,6 +145,20 @@ struct MemberFunctionPointerTraits { using return_type = R; using args_type = ArgList; }; + +template +struct MemberFunctionPointerTraits { + using class_type = Class; + using return_type = R; + using args_type = ArgList; +}; + +template +struct MemberFunctionPointerTraits { + using class_type = Class; + using return_type = R; + using args_type = ArgList; +}; #endif // Traits for simple functor. diff --git a/closure/trivial_tuple.hpp b/closure/trivial_tuple.hpp new file mode 100644 index 0000000..7230649 --- /dev/null +++ b/closure/trivial_tuple.hpp @@ -0,0 +1,98 @@ +// +// Created by Youtao Guo on 2023/2/18. +// + +#pragma once + +#include +#include +#include + +namespace closure { + +namespace tuple { + +template +struct Component { + static_assert(std::is_same>::value, "type Tp is not a decayed type"); + + protected: + constexpr Component() = default; + template , Component>::value, int> = 0> + constexpr explicit Component(Up&& v) : value(std::forward(v)) {} + + Tp value; +}; + +static_assert(std::is_trivially_copyable>::value, ""); + +template +class TupleImpl; + +template +class TupleImpl, Tp...> : private Component... { + template + struct ElementTypeImpl { + static_assert(I < sizeof...(Is), "index out of bound"); + using type = typename ElementTypeImpl::type; + }; + + template + struct ElementTypeImpl { + using type = Tp_; + }; + + protected: + template + struct ElementType { + using type = typename ElementTypeImpl<0, I, Tp...>::type; + }; + + constexpr TupleImpl() = default; + + template + constexpr explicit TupleImpl(Ups&&... ups) : Component(std::forward(ups))... {} + + template + constexpr const typename ElementType::type& Get() const noexcept { + return Component::type>::value; + } + + template + typename ElementType::type& Get() noexcept { + return Component::type>::value; + } +}; + +template +class TrivialTuple : private TupleImpl, Tps...> { + using base = TupleImpl, Tps...>; + + public: + constexpr TrivialTuple() = default; + + template + constexpr explicit TrivialTuple(Ups&&... ups) : base(std::forward(ups)...) {} + + using base::Get; +}; + +static_assert(std::is_trivially_copyable>::value, ""); +static_assert(std::is_trivially_copyable>::value, ""); +static_assert(std::is_trivially_copyable>::value, ""); + +using std::get; + +template +constexpr decltype(auto) get(const TrivialTuple& tuple) noexcept { + return tuple.template Get(); +} + +template +constexpr decltype(auto) get(TrivialTuple& tuple) noexcept { + return tuple.template Get(); +} + +} // namespace tuple + +} // namespace closure \ No newline at end of file diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index fd38a0d..fa7c1f9 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -21,6 +21,7 @@ list(APPEND closure_src ../closure/placeholders.hpp ../closure/util.hpp ../closure/bind.hpp + ../closure/trivial_tuple.hpp ) add_executable(example diff --git a/example/example.cpp b/example/example.cpp index 7f7f8eb..aaa626f 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -1,3 +1,4 @@ +#include #include #include "closure.hpp" diff --git a/test/test.cpp b/test/test.cpp index 11526fd..7b032f8 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -302,17 +302,6 @@ TEST(TestAnyType, Any) { test("123"); } -TEST(TestClosure, EmptyBaseOptimize) { - using c1 = closureimpl::ClosureImpl>; - static_assert(sizeof(c1) == 8, ""); - using agents = std::tuple, placeholders::Agent>; - using c2 = closureimpl::ClosureImpl, placeholders::Getter>>; - static_assert(sizeof(c2) == 8, ""); - using c3 = Closure; - static_assert(sizeof(c3) == 32, ""); -} - std::size_t sum(const int& v1, double v2, int v3, int v4) noexcept { return v1 + v2 + v3 + v4; } int forwarding_test(std::unique_ptr p) noexcept { return *p; } @@ -356,6 +345,31 @@ TEST(TestValidator, InvokeMemberFunction) { static_assert(std::is_same::value, ""); } +TEST(TestClosure, EmptyBaseOptimize) { + using c1 = closureimpl::ClosureImpl>; + static_assert(sizeof(c1) == 8, ""); + using agents = std::tuple, placeholders::Agent>; + using c2 = closureimpl::ClosureImpl, placeholders::Getter>>; + static_assert(sizeof(c2) == 8, ""); + using c3 = Closure; + static_assert(sizeof(c3) == 32, ""); +} + +TEST(TestClosure, TrivialTuple) { + static_assert(std::is_trivially_copyable>::value, ""); + using c1 = closureimpl::ClosureImpl>; + static_assert(std::is_trivially_copyable::value, ""); + + using c2 = std::remove_pointer_t; + static_assert(closureimpl::soo::IsSmallObject::value, ""); + + using c3 = std::remove_pointer_t(), PlaceHolder<1>(), 1))>; + static_assert(closureimpl::soo::IsSmallObject::value, ""); + using c3tuple = typename c3::stored_types; + static_assert(sizeof(c3tuple) == 8, ""); +} + TEST(TestClosure, FunctionPointer) { auto closure1 = MakeClosure(sum, 1); static_assert(std::is_same>::value, ""); @@ -481,7 +495,6 @@ class TestClassBindMethod { TEST(TestClosure, Method) { TestClassBindMethod cl; static_assert(std::is_member_function_pointer::value, ""); - static_assert(traits::IsDereferencable>::value, ""); auto closure1 = MakeClosure(&TestClassBindMethod::ResIntArg0, &cl); EXPECT_EQ(closure1(), 0); @@ -540,9 +553,10 @@ TEST(TestClosure, NonSimpleFunctor) { EXPECT_EQ(closure2(1, 2), 3); struct Simple { - int operator()(int a, int b) const { return a + b; } + int operator()(int a, int b) const volatile { return a + b; } }; closure2 = MakeClosure(Simple{}); // ok + EXPECT_EQ(closure2(7, 8), 15); } TEST(TestClosureWithPlaceHolders, FunctionPointer) {