/** * Copyright 2015-2016 Denis Blank * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* #include #include #include namespace detail { template struct Identity { }; template struct IdentityInheritenceWrapper : Identity, IdentityInheritenceWrapper { }; enum class NamedParameterId { NAMED_PARAMATER_CALLBACK, NAMED_PARAMATER_REJECTOR }; template struct NamedParameter : std::integral_constant, std::common_type { }; template using GetNamedParameterOrDefault = void; template class ContinuableBase; template struct ReturnTypeToContinuableConverter; template struct template class ContinuableBase, CallbackType> { CallbackType callback_; public: explicit ContinuableBase(CallbackType&& callback) : callback_(std::move(callback)) { } void via() { } template auto then(C&& continuation) { // The type the callback will evaluate to using EvaluatedTo = decltype(std::declval()(std::declval()...)); return EvaluatedTo{ }; } }; } // namespace detail using namespace detail; // template // using Continuable = detail::ContinuableBase>; template struct Callback { void operator() (Args... ) { } }; template auto make_continuable(Args&&...) { return Continuable<> { }; } auto http_request(std::string url) { return make_continuable([url](auto& callback) { callback("
hi
"); }); } template auto appendHandlerToContinuation(Continuation&& cont, Handler&& handler) { return [cont = std::forward(cont), handler = std::forward(handler)](auto&& continuation) { using T = decltype(continuation); return [continuation = std::forward(continuation)](auto&&... arg) { continuation(std::forward(arg)...); }; current([continuation = std::forward(continuation)](auto&&... arg) { continuation(std::forward(arg)...); }); }; } */ #include #include #include #include #include #include #include // Equivalent to C++17's std::void_t which is targets a bug in GCC, // that prevents correct SFINAE behavior. // See http://stackoverflow.com/questions/35753920 for details. template struct deduce_to_void : std::common_type { }; template using always_void_t = typename deduce_to_void::type; struct SelfDispatcher { template void operator() (T&& callable) const { std::forward(callable)(); } }; template struct Identity { }; template class ContinuableBase; static auto createEmptyContinuation() { return [](auto&& callback) { callback(); }; } static auto createEmptyCallback() { return [](auto&&...) { }; } template auto applyTuple(std::integer_sequence, T&& tuple, F&& function) { return std::forward(function)(std::get(std::forward(tuple))...); } template auto tupleMerge(std::integer_sequence, std::integer_sequence, std::tuple&& left, std::tuple&& right) { return std::make_tuple(std::get(std::move(left))..., std::get(std::move(right))...); } /// Merges the left and the right tuple together in one. template auto tupleMerge(std::tuple&& left, std::tuple&& right) { return tupleMerge(std::make_integer_sequence{}, std::make_integer_sequence{}, std::move(left), std::move(right)); } class Ownership { public: Ownership() { } explicit Ownership(bool isOwning_) : isOwning(isOwning_) { } Ownership(Ownership const&) = default; explicit Ownership(Ownership&& right) noexcept : isOwning(std::exchange(right.isOwning, false)) { }; Ownership& operator = (Ownership const&) = default; Ownership& operator = (Ownership&& right) noexcept { isOwning = std::exchange(right.isOwning, false); return *this; } Ownership operator&& (Ownership right) const { return Ownership(hasOwnership() && right.hasOwnership()); } bool hasOwnership() const noexcept { return isOwning; } void invalidate() { isOwning = false; } private: bool isOwning{ true }; }; template struct undecorate_function; template struct undecorate_function { /// The return type of the function. typedef ReturnType return_type; /// The argument types of the function as pack in Identity. typedef Identity argument_type; }; /// Mutable function pointers template struct undecorate_function : undecorate_function { }; /// Const function pointers template struct undecorate_function : undecorate_function { }; /// Mutable class method pointers template struct undecorate_function : undecorate_function { }; /// Const class method pointers template struct undecorate_function : undecorate_function { }; /// Mutable volatile class method pointers template struct undecorate_function : undecorate_function { }; /// Const volatile class method pointers template struct undecorate_function : undecorate_function { }; template using do_undecorate = std::conditional_t< std::is_class::value, decltype(&Function::operator()), Function>; template> struct is_undecorateable : std::false_type { }; template struct is_undecorateable::return_type >> : std::true_type { }; /// Decorates single values template struct CallbackResultDecorator { template static auto decorate(Callback&& callback) { return [callback = std::forward(callback)](auto&&... args) { Value value = callback(std::forward(args)...); return [value = std::move(value)](auto&& callback) mutable { callback(std::move(value)); }; }; } }; /// No decoration is needed for continuables template struct CallbackResultDecorator>{ template static auto decorate(Callback&& callback) -> std::decay_t { return std::forward(callback); } }; /// Decorates void as return type template<> struct CallbackResultDecorator { template static auto decorate(Callback&& callback) { return [callback = std::forward(callback)](auto&&... args) { callback(std::forward(args)...); return createEmptyContinuation(); }; } }; // Decorates tuples as return type template struct CallbackResultDecorator> { template static auto decorate(Callback&& callback) { return [callback = std::forward(callback)](auto&&... args) { // Receive the tuple from the callback auto result = callback(std::forward(args)...); return [result = std::move(result)] (auto&& next) mutable { // Generate a sequence for tag dispatching auto constexpr const sequence = std::make_integer_sequence{}; // Invoke the callback with the tuple returned // from the previous callback. applyTuple(sequence, std::move(result), std::forward(next)); }; }; } }; /// Create the proxy callback that is responsible for invoking /// the real callback and passing the next continuation into /// the result of the following callback. template auto createProxyCallback(Callback&& callback, Next&& next) { return [callback = std::forward(callback), next = std::forward(next)] (auto&&... args) mutable { // Callbacks shall always return a continuation, // if not, we need to decorate it. using Result = decltype(callback(std::forward(args)...)); using Decorator = CallbackResultDecorator; Decorator::decorate(std::move(callback)) (std::forward(args)...)(std::move(next)); }; } template auto appendCallback(Continuation&& continuation, Callback&& callback) { return [continuation = std::forward(continuation), callback = std::forward(callback)](auto&& next) mutable { // Invoke the next invocation handler std::move(continuation)(createProxyCallback( std::move(callback), std::forward(next))); }; } template void invokeContinuation(Data data) { // Check whether the ownership is acquired and start the continuation call if (data.ownership.hasOwnership()) { // Pass an empty callback to the continuation to invoke it std::move(data.continuation)(createEmptyCallback()); } } template struct ContinuableConfig { using Continuation = ContinuationType; using Dispatcher = DispatcherType; template using ChangeContinuationTo = ContinuableConfig< NewType, Dispatcher >; template using ChangeDispatcherTo = ContinuableConfig< Continuation, NewType >; }; /// template struct ContinuableData { using Config = ConfigType; ContinuableData(Ownership ownership_, typename Config::Continuation continuation_, typename Config::Dispatcher dispatcher_) noexcept : ownership(std::move(ownership_)), continuation(std::move(continuation_)), dispatcher(std::move(dispatcher_)) { } ContinuableData(typename Config::Continuation continuation_, typename Config::Dispatcher dispatcher_) noexcept : continuation(std::move(continuation_)), dispatcher(std::move(dispatcher_)) { } Ownership ownership; typename Config::Continuation continuation; typename Config::Dispatcher dispatcher; }; /// The DefaultDecoration is a container for already materialized /// ContinuableData which can be accessed instantly. template class DefaultDecoration { public: explicit DefaultDecoration(Data data_) : data(std::move(data_)) { } using Config = typename Data::Config; /// Return a r-value reference to the data template Data&& undecorate()&& { return std::move(data); } /// Return a copy of the data template Data undecorate() const& { return data; } private: Data data; }; template auto undecorateCombined(Identity, std::tuple combined) { } template auto undecorateCombined(std::tuple combined) { // using TargetArgs = typename do_undecorate::argument_type; // return undecorateCombined(TargetArgs{}, std::move(combined)); } template class LazyCombineDecoration { public: // TODO explicit LazyCombineDecoration(Combined combined_) : combined(std::move(combined_)) { } // using Config = typename Data::Config; template static void requiresUndecorateable() { static_assert(is_undecorateable::value, "Can't retrieve the signature of the given callback. " "Consider to pass an untemplated function or functor " "to the `then` method invocation to fix this."); } /// Return a r-value reference to the data template void undecorate()&& { requiresUndecorateable(); return undecorateCombined(std::move(combined)); } template /// Return a copy of the data void undecorate() const& { requiresUndecorateable(); return undecorateCombined(combined); } template auto merge(RightLazyCombine right)&& { auto merged = tupleMerge(std::move(combined), std::move(right.combined)); return ContinuableBase> { LazyCombineDecoration{std::move(merged)} }; } private: Combined combined; }; template auto make_continuable(Continuation&& continuation, Dispatcher&& dispatcher = SelfDispatcher{}) noexcept { using Decoration = DefaultDecoration, std::decay_t >>>; return ContinuableBase(Decoration({ std::forward(continuation), std::forward(dispatcher) })); } template auto thenImpl(Data data, Callback&& callback) { auto next = appendCallback(std::move(data.continuation), std::forward(callback)); using Decoration = DefaultDecoration >>; return ContinuableBase(Decoration({ std::move(data.ownership), std::move(next), std::move(data.dispatcher) })); } template auto postImpl(Data data, NewDispatcher&& newDispatcher) { using Decoration = DefaultDecoration> >>; return ContinuableBase(Decoration({ std::move(data.ownership), std::move(data.continuation), std::forward(newDispatcher) })); } template auto toLazyCombined(LazyCombineDecoration decoration) { return std::move(decoration); } template auto toLazyCombined(Decoration&& decoration) { auto data = std::forward(decoration).template undecorate(); return LazyCombineDecoration>(std::move(data)); } template auto combineImpl(LeftDecoration&& leftDecoration, RightDecoration&& rightDecoration) { return toLazyCombined(std::forward(leftDecoration)) .merge(toLazyCombined(std::forward(rightDecoration))); } template class ContinuableBase { template friend class ContinuableBase; public: explicit ContinuableBase(Decoration decoration_) : decoration(std::move(decoration_)) { } ~ContinuableBase() { // Undecorate/materialize the decoration // invokeContinuation(std::move(decoration).template undecorate()); } ContinuableBase(ContinuableBase&&) = default; ContinuableBase(ContinuableBase const&) = default; template auto then(Callback&& callback)&& { return thenImpl(std::move(decoration).template undecorate(), std::forward(callback)); } template auto then(Callback&& callback) const& { return thenImpl(decoration.template undecorate(), std::forward(callback)); } /*template auto post(NewDispatcher&& newDispatcher)&& { return postImpl(std::move(decoration).template undecorate(), std::forward(newDispatcher)); } template auto post(NewDispatcher&& newDispatcher) const& { return postImpl(decoration.template undecorate(), std::forward(newDispatcher)); }*/ template auto operator&& (ContinuableBase right)&& { return combineImpl(std::move(decoration), std::move(right.decoration)); } template auto operator&& (ContinuableBase right) const& { return combineImpl(decoration, std::move(right.decoration)); } template auto undecorateFor(Callback&&) { return decoration.template undecorate(); } private: /// The Decoration represents the possible lazy materialized /// data of the continuable. /// The decoration pattern is used to make it possible to allow lazy chaining /// of operators on Continuables like the and expression `&&`, /// that requires lazy evaluation and the signature of the callback chained /// with ContinuableBase::then. Decoration decoration; }; static auto makeTestContinuation() { return make_continuable([i = std::make_unique(0)](auto&& callback) { callback("47"); }); } struct Inspector { template auto operator() (Args...) { return std::common_type>{}; } }; template struct FailIfWrongArgs { template auto operator() (Args...) -> std::enable_if_t { } }; int main(int, char**) { auto dispatcher = SelfDispatcher{}; /*(makeTestContinuation() && makeTestContinuation()) .undecorateFor([]() { });*/ /*auto unwrapper = [](auto&&... args) { return std::common_type>{}; };*/ // using T = decltype(unwrap(FailIfWrongArgs<0>{})); // using T = decltype(unwrap(std::declval())); // T t{}; // auto combined = makeTestContinuation() && makeTestContinuation(); int res = 0; makeTestContinuation() .then([](std::string) { return std::make_tuple(47, 46, 45); }) // .post(dispatcher) .then([](int val1, int val2, int val3) { return val1 + val2 + val3; }) .then([&](int val) { res += val; }) .then([] { return makeTestContinuation(); }) .then([] (std::string arg) { }); return res; }