// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/utils/common.h" #include "td/utils/invoke.h" #include "td/utils/logging.h" #include #include #include #include // // Essentially we have: // (ActorT::func, arg1, arg2, ..., argn) // We want to call: // actor->func(arg1, arg2, ..., argn) // And in some cases we would like to delay this call. // // First attempt would be // [a1=arg1, a2=arg2, ..., an=argn](ActorT *actor) { // actor->func(a1, a2, ..., an) // } // // But there are some difficulties with elimitation on unnecessary copies. // We want to use move constructor when it is possible // // We may pass // Tmp. Temporary / rvalue reference // Var. Variable / reference // CnstRef. const reference // // // Function may expect // Val. Value // CnstRef. const reference // Ref. rvalue reverence / reference // // TODO: // Immediate call / Delayed call // Tmp->Val move / move->move // Tmp->CnstRef + / move->+ // Tmp->Ref + / move->+ // Var->Val copy / copy->move // Var->CnstRef + / copy-> // Var->Ref + / copy->+ // khm. It will complile, but won't work // // So I will use common idiom: forward references // If delay is needed, just std::forward data to temporary storage, and std::move them when call is executed. // // // create_immediate_closure(&ActorT::func, arg1, arg2, ..., argn).run(actor) // to_delayed_closure(std::move(immediate)).run(actor) namespace td { template class DelayedClosure; template class ImmediateClosure { public: using Delayed = DelayedClosure; friend Delayed; using ActorType = ActorT; // no &&. just save references as references. explicit ImmediateClosure(FunctionT func, ArgsT... args) : args(func, std::forward(args)...) { } private: std::tuple args; public: auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) { return mem_call_tuple(actor, std::move(args)); } }; template ImmediateClosure create_immediate_closure( ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&... args) { return ImmediateClosure(func, std::forward(args)...); } template class DelayedClosure { public: using ActorType = ActorT; using Delayed = DelayedClosure; DelayedClosure clone() const { return do_clone(*this); } explicit DelayedClosure(ImmediateClosure &&other) : args(std::move(other.args)) { } explicit DelayedClosure(FunctionT func, ArgsT... args) : args(func, std::forward(args)...) { } template void for_each(const F &f) { tuple_for_each(args, f); } private: using ArgsStorageT = std::tuple::type...>; ArgsStorageT args; template explicit DelayedClosure(const DelayedClosure &other, std::enable_if_t::value...>::value, int> = 0) : args(other.args) { } template explicit DelayedClosure( const DelayedClosure &other, std::enable_if_t::value...>::value, int> = 0) { LOG(FATAL) << "Deleted constructor"; std::abort(); } template std::enable_if_t::value...>::value, DelayedClosure> do_clone(const DelayedClosure &value) const { LOG(FATAL) << "Trying to clone DelayedClosure that contains noncopyable elements"; std::abort(); } template std::enable_if_t::value...>::value, DelayedClosure> do_clone(const DelayedClosure &value) const { return DelayedClosure(value); } public: auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) { return mem_call_tuple(actor, std::move(args)); } }; template typename ImmediateClosure::Delayed to_delayed_closure(ImmediateClosure &&other) { return typename ImmediateClosure::Delayed(std::move(other)); } template DelayedClosure to_delayed_closure(DelayedClosure &&other) { return std::move(other); } template auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&... args) { return DelayedClosure(func, std::forward(args)...); } } // namespace td