From 17ad6d3bc1014b454412436d959289a046f3ce31 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Wed, 1 Mar 2017 01:42:34 +0100 Subject: [PATCH] Add more documentation --- CMakeLists.txt | 18 +- Readme.md | 4 +- dep/CMakeLists.txt | 9 +- include/continuable/continuable-base.hpp | 206 ++++++++++++++++++-- include/continuable/continuable-testing.hpp | 7 + include/continuable/continuable.hpp | 7 + 6 files changed, 230 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e5b11..4bb0f3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,19 +5,18 @@ project(continuable C CXX) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) -# Continuable -add_library(continuable INTERFACE) +# continuable-base +add_library(continuable-base INTERFACE) -target_include_directories(continuable +target_include_directories(continuable-base INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include") -target_link_libraries(continuable +target_link_libraries(continuable-base INTERFACE - function2 Threads::Threads) -target_compile_features(continuable +target_compile_features(continuable-base INTERFACE cxx_alias_templates cxx_auto_type @@ -33,6 +32,13 @@ target_compile_features(continuable cxx_trailing_return_types cxx_return_type_deduction) +add_library(continuable INTERFACE) + +target_link_libraries(continuable + INTERFACE + continuable-base + function2) + # Testing if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) if (MSVC) diff --git a/Readme.md b/Readme.md index 6c1008f..8336f18 100644 --- a/Readme.md +++ b/Readme.md @@ -8,8 +8,8 @@ This library provides full feature support of: * async continuation chaining using **callbacks** (*then*). * **no enforced type-erasure** which means we need **extremely fewer heap allocations** . -* support for **finite logical connections** between continuables through an **all or any** strategy. -* **syntactic sugar** for attaching callbacks to a continuation. +* support for **finite logical connections** between continuables through an **all, any or sequence** strategy. +* **syntactic sugar** for attaching callbacks to a continuation like partial invocation. diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index 26bdded..3fd7d38 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -1,2 +1,7 @@ -add_subdirectory(googletest) -add_subdirectory(function2) +if(NOT TARGET gtest) + add_subdirectory(googletest) +endif() + +if(NOT TARGET function2) + add_subdirectory(function2) +endif() diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index a45ce6a..ed41c47 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -1,5 +1,12 @@ /** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + + Copyright(c) 2015 - 2017 Denis Blank Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,6 +33,7 @@ #include #include +#include #include #include #include @@ -33,16 +41,47 @@ #include #include +/// Declares the continuable library namespace. +/// +/// The most important class is cti::continuable_base, that provides the +/// whole functionality for continuation chaining. +/// +/// The class cti::continuable_base is created through the +/// cti::make_continuable() function which accepts a callback taking function. +/// +/// Also there are following support functions available: +/// - cti::all_of() - connects cti::continuable_base's to an `all` connection. +/// - cti::any_of() - connects cti::continuable_base's to an `any` connection. +/// - cti::seq_of() - connects cti::continuable_base's to a sequence. +/// namespace cti { /// \cond false inline namespace abi_v1 { /// \endcond -/// A wrapper class to mark a functional class as continuation -/// Such a wrapper class is required to decorate the result of a callback -/// correctly. +/// The main class of the continuable library, it provides the functionality +/// for chaining callbacks and continuations together to a unified hierarchy. +/// +/// The most important method is the cti::continuable_base::then() method, +/// which allows to attach a callback to the continuable. +/// +/// \tparam Data The internal data which is used to store the current +/// continuation and intermediate lazy connection result. +/// +/// \tparam Annotation The internal data used to store the current signature +/// hint or strategy used for combining lazy connections. +/// +/// \note Nearly all methods of the cti::continuable_base are required to be +/// called as r-value. This is required because the continuable carries +/// variables which are consumed when the object is transformed as part +/// of a method call. You may copy a continuable which underlying +/// storages are copyable to split the call hierarchy into multiple parts. +/// +/// \since version 1.0.0 template class continuable_base; +/// Declares the internal private namespace of the continuable library +/// which isn't intended to be used by users of the library. namespace detail { /// Utility namespace which provides useful meta-programming support namespace util { @@ -747,6 +786,8 @@ inline auto sequencedUnpackInvoker() { std::forward(args)...); util::unpack(std::move(result), [&](auto&&... types) { + /// TODO Add inplace resolution here + std::forward(nextCallback)( std::forward(types)...); }); @@ -1358,22 +1399,29 @@ auto as_future(continuable_base&& continuable) { } // end namespace detail template class continuable_base { + /// \cond false template friend class continuable_base; friend struct detail::base::attorney; // The continuation type or intermediate result Data data_; detail::util::ownership ownership_; + /// \endcond public: + /// Constructor accepting the data object while erasing the annotation explicit continuable_base(Data data) : data_(std::move(data)) {} - /// Constructor taking the data + /// Constructor accepting the any object convertible to the data object, + /// while erasing the annotation template , Data>::value>* = nullptr> continuable_base(OData&& data) : data_(std::forward(data)) {} - /// Constructor taking the data of other continuables while erasing the hint + /// Constructor taking the data of other continuables while erasing the hint. + /// + /// This constructor makes it possible to replace the internal data object of + /// the continuable by any object which is useful for type-erasure. template continuable_base(continuable_base&& other) : continuable_base(std::move(other).materialize().consumeData()) {} @@ -1395,6 +1443,8 @@ public: /// You may release the continuable_base through calling the corresponding /// continuable_base::release() method which prevents /// the invocation on destruction. + /// + /// \since version 1.0.0 ~continuable_base() { if (ownership_.has_ownership()) { std::move(*this).done(); @@ -1402,12 +1452,87 @@ public: assert(!ownership_.has_ownership() && "Ownership should be released!"); } - template - auto then(continuable_base&& continuation) && { - return std::move(*this).then( - detail::base::wrap_continuation(std::move(continuation).materialize())); - } - + /// Main method of the continuable_base to chain the current continuation + /// with a new callback. + /// + /// \param callback The callback which is used to process the current + /// asynchronous result on arrival. The callback is required to accept + /// the current result at least partially (or nothing of the result). + /// ```cpp + /// (http_request("github.com") && http_request("atom.io")) + /// .then([](std::string github, std::string atom) { + /// // We use the whole result + /// }); + /// + /// (http_request("github.com") && http_request("atom.io")) + /// .then([](std::string github) { + /// // We only use the result partially + /// }); + /// + /// (http_request("github.com") && http_request("atom.io")) + /// .then([] { + /// // We discard the result + /// }); + /// ``` + /// + /// \param executor The optional executor which is used to dispatch + /// the callback. The executor needs to accept functional objects + /// callable through an `operator()` through its operator() itself. + /// The executor can be move-only, but it's not required to. + /// The default executor which is used when omitting the argument + /// dispatches the callback on the current executing thread. + /// Consider the example shown below: + /// ```cpp + /// auto executor = [](auto&& work) { + /// // Dispatch the work here or forward it to an executor of + /// // your choice. + /// std::forward(work)(); + /// }; + /// + /// http_request("github.com") + /// .then([](std::string github) { + /// // Do something... + /// }, executor); + /// ``` + /// + /// \returns Returns a continuable_base with an asynchronous return type + /// depending on the return value of the callback: + /// | Callback returns | Resulting type | + /// | : ---------------------- : | : --------------------------------------- | + /// | `void` | `continuable_base with <>` | + /// | `Arg` | `continuable_base with ` | + /// | `std::pair` | `continuable_base with ` | + /// | `std::tuple` | `continuable_base with ` | + /// | `continuable_base` | `continuable_base with ` | + /// Which means the result type of the continuable_base is equal to + /// the plain types the callback returns (`std::tuple` and + /// `std::pair` arguments are unwrapped). + /// A single continuable_base as argument is resolved and the result + /// type is equal to the resolved continuable_base. + /// Consider the following examples: + /// ```cpp + /// http_request("github.com") + /// .then([](std::string github) { return; }) + /// .then([] { }); // + /// + /// http_request("github.com") + /// .then([](std::string github) { return 0; }) + /// .then([](int a) { }); // + /// + /// http_request("github.com") + /// .then([](std::string github) { return std::make_pair(1, 2); }) + /// .then([](int a, int b) { }); + /// + /// http_request("github.com") + /// .then([](std::string github) { return std::make_tuple(1, 2, 3); }) + /// .then([](int a, int b, int c) { }); + /// + /// http_request("github.com") + /// .then([](std::string github) { return http_request("atom.io"); }) + /// .then([](std::string atom) { }); + /// ``` + /// + /// \since version 1.0.0 template auto then(T&& callback, E&& executor = detail::this_thread_executor_tag{}) && { @@ -1416,6 +1541,65 @@ public: std::forward(executor)); } + /// Additional overload of the continuable_base::then() method + /// which is accepting a continuable_base itself. + /// + /// \param continuation A continuable_base reflecting the continuation to + /// which is used to continue the call hierarchy. + /// The result of the current continuable is discarded and the given + /// continuation is invoked as shown below. + /// ```cpp + /// http_request("github.com") + /// .then(http_request("atom.io")) + /// .then([](std::string atom) { + /// // ... + /// }); + /// ``` + /// + /// \returns Returns a continuable_base representing the next asynchronous + /// result to continue within the asynchronous call hierarchy. + /// + /// \since version 1.0.0 + template + auto then(continuable_base&& continuation) && { + return std::move(*this).then( + detail::base::wrap_continuation(std::move(continuation).materialize())); + } + + /// Connects both continuable_base objects logically and calls any attached + /// callback with the result of both continuables. + /// + /// \param right The continuable on the right-hand side to connect. + /// + /// \returns Returns a continuable_base with a result type matching + /// the result of the left continuable_base combined with the + /// right continuable_base. + /// The returned continuable_base will be in an intermediate lazy + /// state, further calls to its continuable_base::operator && + /// will add other continuable_base objects to the current + /// invocation chain. + /// ```cpp + /// (http_request("github.com") && http_request("atom.io")) + /// .then([](std::string github, std::string atom) { + /// // ... + /// }); + /// + /// auto request = http_request("github.com") && http_request("atom.io"); + /// (std::move(request) && http_request("travis-ci.org")) + /// // All three requests are invoked in parallel although we added + /// // the request to "travis-ci.org" last. + /// .then([](std::string github, std::string atom, std::string travis) { + /// // ... + /// }); + /// ``` + /// + /// \note The continuables are invoked parallel on the current thread, + /// because the `all` strategy tries to resolve the continuations + /// as fast as possible. + /// Sequential invocation is also supported through the + /// continuable_base::operator>> method. + /// + /// \since version 1.0.0 template auto operator&&(continuable_base&& right) && { right.assert_owning(); diff --git a/include/continuable/continuable-testing.hpp b/include/continuable/continuable-testing.hpp index 8338f9c..e71f022 100644 --- a/include/continuable/continuable-testing.hpp +++ b/include/continuable/continuable-testing.hpp @@ -1,5 +1,12 @@ /** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + + Copyright(c) 2015 - 2017 Denis Blank Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/include/continuable/continuable.hpp b/include/continuable/continuable.hpp index f2d9c3e..075579d 100644 --- a/include/continuable/continuable.hpp +++ b/include/continuable/continuable.hpp @@ -1,5 +1,12 @@ /** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + + Copyright(c) 2015 - 2017 Denis Blank Permission is hereby granted, free of charge, to any person obtaining a copy