commit 867801d20209e82fab1dbc7b204b14267a1e12da Author: Denis Blank Date: Sat Oct 8 12:16:16 2016 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce0efbb --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studo 2015 cache/options directory +.vs/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c2d2a31 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dep/googletest/googletest"] + path = dep/googletest/googletest + url = https://github.com/google/googletest.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dbf0e84 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.2) +project(continuable CXX) + +set(continuable_VERSION_MAJOR 1) + +add_library(continuable INTERFACE) + +target_include_directories(continuable + INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/include") + +target_compile_features(continuable + INTERFACE + cxx_alias_templates + cxx_auto_type + cxx_constexpr + cxx_decltype + cxx_decltype_auto + cxx_final + cxx_lambdas + cxx_generic_lambdas + cxx_variadic_templates + cxx_defaulted_functions + cxx_nullptr + cxx_trailing_return_types) + +# Set up the test environment for testing +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + if (MSVC) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + endif() + + enable_testing() + + include(cmake/CMakeLists.txt) + + add_subdirectory(dep) + add_subdirectory(test) +endif () diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..5182205 --- /dev/null +++ b/Readme.md @@ -0,0 +1,68 @@ +# Continuable +[![Build Status](https://travis-ci.org/Naios/Continuable.svg?branch=master)](https://travis-ci.org/Naios/Continuable) + +> C++11 Continuation chains (Work in progress) + +This library aims to provide full support for **async continuation chains with callbacks**. + +*** + +**Important note:** Everything is work in progress and the library is not ready to be use yet, most showed examples are not working yet and are just there for explaining my plans. + +## Creating Continuables + +#### Create a continuable from a callback taking function + +```c++ +Continuable continuable = + make_continuable([](Callback&& callback) + { + callback("some data"); + }); + +``` + +#### Providing helper functions + +```c++ +Continuable mysql_query(std::string&& query) +{ + return make_continuable( + [query = std::move(query)](Callback&& callback) mutable + { + // Pass the callback to the handler + // which calls the callback when finished. + // Everything which takes a callback works with continuables. + mysql_handle_async_query(std::move(query), std::move(callback)); + }); +} + +// You can use the helper function like you would normally do: +mysql_query("DELETE FROM users WHERE id = 27361"); + +// Or using chaining to handle the result which is covered in the next topic . +mysql_query("SELECT id, name FROM users") + .then([](ResultSet result) + { + }); +``` + +## Chaining Continuables + +Chaining continuables is very easy: + +```c++ +(mysql_query("SELECT id, name FROM users") + && http_request("http://github.com")) + .then([](ResultSet result, std::string page_content) + { + // Pass one argument to the next continuation... + return page_content.empty(); + + // ... or pass multiple args using tuples... + return std::make_tuple(std::move(result), page_content.empty()); + + // ... return the next continuable to process + return mysql_query("SELECT id name FROM sessions"); + }); +``` diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000..df8ff04 --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1 @@ +include(cmake/configure-compiler.cmake) diff --git a/cmake/compiler/clang.cmake b/cmake/compiler/clang.cmake new file mode 100644 index 0000000..31679f4 --- /dev/null +++ b/cmake/compiler/clang.cmake @@ -0,0 +1,2 @@ +# Enable full warnings +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra") diff --git a/cmake/compiler/gcc.cmake b/cmake/compiler/gcc.cmake new file mode 100644 index 0000000..31679f4 --- /dev/null +++ b/cmake/compiler/gcc.cmake @@ -0,0 +1,2 @@ +# Enable full warnings +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra") diff --git a/cmake/compiler/msvc.cmake b/cmake/compiler/msvc.cmake new file mode 100644 index 0000000..2650192 --- /dev/null +++ b/cmake/compiler/msvc.cmake @@ -0,0 +1,11 @@ +if(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(PLATFORM 64) +else() + set(PLATFORM 32) +endif() + +if (PLATFORM EQUAL 64) + add_definitions("-D_WIN64") +endif() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP") diff --git a/cmake/configure-compiler.cmake b/cmake/configure-compiler.cmake new file mode 100644 index 0000000..8a1b89b --- /dev/null +++ b/cmake/configure-compiler.cmake @@ -0,0 +1,11 @@ +# Select the compiler specific cmake file +set(MSVC_ID "MSVC") +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake) +elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc.cmake) +elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID}) + include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc.cmake) +else() + message(FATAL_ERROR "Unknown compiler!") +endif() diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt new file mode 100644 index 0000000..73deebc --- /dev/null +++ b/dep/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(googletest) diff --git a/dep/googletest/CMakeLists.txt b/dep/googletest/CMakeLists.txt new file mode 100644 index 0000000..0a92797 --- /dev/null +++ b/dep/googletest/CMakeLists.txt @@ -0,0 +1,37 @@ +add_library(gtest STATIC + ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc) + +target_include_directories(gtest + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/include) + +target_compile_definitions(gtest + PUBLIC + -DGTEST_HAS_PTHREAD=0 + -DGTEST_LANG_CXX11=1) + +target_compile_features(gtest + PUBLIC + ${CXX11_FEATURES}) + +add_library(gtest-main STATIC + ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest_main.cc) + +target_link_libraries(gtest-main + PUBLIC + gtest) + +add_library(gmock STATIC + ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/src/gmock-all.cc) + +target_link_libraries(gmock + PUBLIC + gtest) + +target_include_directories(gmock + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/include) diff --git a/dep/googletest/googletest b/dep/googletest/googletest new file mode 160000 index 0000000..ecd5308 --- /dev/null +++ b/dep/googletest/googletest @@ -0,0 +1 @@ +Subproject commit ecd530865cefdfa7dea58e84f6aa1b548950363d diff --git a/include/continuable/continuable.hpp b/include/continuable/continuable.hpp new file mode 100644 index 0000000..e2abf36 --- /dev/null +++ b/include/continuable/continuable.hpp @@ -0,0 +1,657 @@ + +/** + * 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. + */ + +#ifndef CONTINUABLE_HPP_INCLUDED__ +#define CONTINUABLE_HPP_INCLUDED__ + + /* +#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; +} + +#endif // CONTINUABLE_HPP_INCLUDED__ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..961d8e9 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(continuable-tests + ${CMAKE_CURRENT_LIST_DIR}/test-chaining.cpp) + +target_link_libraries(continuable-tests + PRIVATE + continuable + gtest-main) + +add_test(NAME continuable-unit-tests COMMAND continuable-tests) diff --git a/test/test-chaining.cpp b/test/test-chaining.cpp new file mode 100644 index 0000000..4780a9f --- /dev/null +++ b/test/test-chaining.cpp @@ -0,0 +1,22 @@ +/** +* Copyright 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 "gtest/gtest.h" +#include "continuable/continuable.hpp" + +TEST(ContinuableBaseTests, ContinuablesAreInstantiable) { + EXPECT_TRUE(true); +}