diff --git a/LICENSE b/LICENSE index 0e111be..c607547 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2015-2017 Denis Blank +Copyright (c) 2015 - 2017 Denis Blank Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Readme.md b/Readme.md index 8336f18..33d9723 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,8 @@ -# continuable->then(make_things_simple()); +![](https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif) -[![Build Status](https://travis-ci.org/Naios/continuable.svg?branch=master)](https://travis-ci.org/Naios/continuable) [![Build status](https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv?svg=true)](https://ci.appveyor.com/project/Naios/continuable) ![](https://img.shields.io/badge/License-MIT-blue.svg) [![](https://img.shields.io/badge/Try-online-green.svg)](http://melpon.org/wandbox/permlink/gRWxSNHtARvRcmSY) +![](https://img.shields.io/badge/Release-v1.0.0-0091EA.svg) [![Build Status](https://travis-ci.org/Naios/continuable.svg?branch=master)](https://travis-ci.org/Naios/continuable) [![Build status](https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv?svg=true)](https://ci.appveyor.com/project/Naios/continuable) ![](https://img.shields.io/badge/License-MIT-00838F.svg) [![](https://img.shields.io/badge/Documentation-Doxygen-26A69A.svg)](https://naios.github.io/continuable/) [![](https://img.shields.io/badge/Try-online-4DB6AC.svg)](http://melpon.org/wandbox/permlink/xVM2szjDLEge3YLV) + +------ > Async C++14 platform independent continuation chainer providing light and allocation aware futures @@ -9,9 +11,30 @@ 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, any or sequence** strategy. -* **syntactic sugar** for attaching callbacks to a continuation like partial invocation. +* **syntactic sugar** for attaching callbacks to a continuation like partial invocation or tuple unpacking. +> **Note:** This library only provides the facility for building asynchronous functions. Thus functions shown in the examples and the documentation like `http_request`, `mysql_query` or `read_file` aren't provided by this library. + +## Table of contents + +- [The library design](#the-library-design) +- [Installation](#installation) + - [How-to use](#how-to-use) + - [Building the unit-tests](#building-the-unit-tests) +- [Stability and version](#stability-and-version) +- [Quick reference](#quick-reference) + - [Creating Continuables](#creating-continuables) + - [Chaining Continuables](#chaining-continuables) + - [Providing helper functions](#providing-helper-functions) + - [Connecting Continuables {all, any or sequential}](#connecting-continuables-all-any-or-sequential) + - [Partial argument application](#partial-argument-application) + - [Dispatching callbacks through a specific executor](#dispatching-callbacks-through-a-specific-executor) + - [Type erasure](#type-erasure) + - [Future conversion](#future-conversion) +- [Compatibility](#compatibility) +- [Similar implementations and alternatives](#similar-implementations-and-alternatives) +- [License](#license) ## The library design @@ -29,7 +52,7 @@ The continuable library was designed in order to provide you as much as flexibil ## Installation -### Inclusion +### How-to use As mentioned earlier the library is header-only. There is a cmake project provided for simple setup: @@ -46,7 +69,7 @@ add_subdirectory(continuable) target_link_libraries(my_project continuable) ``` -On POSIX you are required to link your application against a corresponding thread library, otherwise `std:: future's` won't work properly, this is done automatically by the provided cmake project. +On POSIX platforms you are required to link your application against a corresponding thread library, otherwise `std::future's` won't work properly, this is done automatically by the provided cmake project. ### Building the unit-tests @@ -58,14 +81,14 @@ git clone --recursive https://github.com/Naios/continuable.git ``` ## Stability and version -Currently, the library is in the incubation state, it provides a stable functionality as the CI unit tests indicate. +The library follows the rules of [semantic versioning](http://semver.org/), the API is kept stable across minor versions. -The API isn't fixed right now and will be eventually changed into the future. - -Also, the unit-test is observed with the Clang sanitizers (asan, ubsan and lsan - when compiling with Clang) or Valgrind (when compiling with GCC). +The CI driven unit-tests are observed through the Clang sanitizers (asan, ubsan and lsan - when compiling with Clang) or Valgrind (when compiling with GCC). ## Quick reference +This chapter only overflies the functionality of the continuable library, the full documentation is located at https://naios.github.io/continuable/. + ### Creating Continuables Create a continuable from a callback taking function: @@ -115,6 +138,7 @@ mysql_query("SELECT `id`, `name` FROM `users`") }); ``` +> **Note:** The continuation chain is invoked when the object is destructed or the `done()` method is called. ### Providing helper functions @@ -144,12 +168,14 @@ mysql_query("SELECT `id`, `name` FROM users") }); ``` -### Connecting Continuables {all or any} + +### Connecting Continuables {all, any or sequential} Continuables provide the operators **&&** and **||** for logical connection: -* **&&** invokes the final callback with the compound result of all connected continuables. -* **||** invokes the final callback once with the first result available. +* **&&** invokes the final callback with the compound result of all connected continuables, the continuables were invoked in parallel. +* **||** invokes the final callback once with the first result which becomes available. +* **>\>** invokes the final callback with the compound result of all connected continuables but the continuations were invokes sequentially. ```C++ auto http_request(std::string url) { @@ -170,6 +196,12 @@ auto http_request(std::string url) { // The callback is called with the first response of either github, travis or atom. }); +// `sequence` of connections: +(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io")) + .then([](std::string github, std::string travis, std::string atom) { + // The requests are invoked sequentially + }); + // mixed logical connections: (http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io"))) .then([](std::string github, std::string travis_or_atom) { @@ -180,9 +212,40 @@ auto http_request(std::string url) { // There are helper functions for connecting continuables: auto all = cti::all_of(http_request("github.com"), http_request("travis-ci.org")); auto any = cti::any_of(http_request("github.com"), http_request("travis-ci.org")); +auto seq = cti::seq_of(http_request("github.com"), http_request("travis-ci.org")); ``` -Logical connections are ensured to be **thread-safe** and **wait-free** by library design (when assuming that *std::call_once* is wait-free - which depends on the toolchain). +> **Note:** Logical connections are ensured to be **thread-safe** and **wait-free** by library design (when assuming that *std::call_once* is wait-free - which depends on the toolchain). + +### Partial argument application + +The callback is called only with the arguments it's accepting: + +```c++ +(http_request("github.com") && read_file("entries.csv")) + .then([] { + // ^^^^^^ The original signature was , + // however, the callback is only invoked with the amount of + // arguments it's accepting. + }); +``` + +### Dispatching callbacks through a specific executor + +Dispatching a callback through a specific executor is supported through through the second argument of `then()`: + +```c++ +auto executor = [](auto&& work) { + // Dispatch the work here, store it for later invocation or move it to another thread. + std::forward(work)(); +}; + +read_file("entries.csv") + .then([](Buffer buffer) { + // ... + }, executor); +// ^^^^^^^^ +``` ### Type erasure @@ -211,13 +274,13 @@ However you may still define your own continuation wrapper with the backend of y ```c++ template -using mycontinuation = cti::continuable_of_t< - cti::continuable_erasure_of_t, - Args...>; +using my_continuation = typename cti::continuable_trait< + std::function, std::function, Args... +>::continuable; // ... -mycontinuation myc = cti::make_continuable([](auto&& callback) { +my_continuation myc = cti::make_continuable([](auto&& callback) { // ^^^^^ // Signatures may be omitted for continuables which are type erased callback(0); @@ -228,7 +291,7 @@ We could also think about using `std::future` as backend but this is even worse ### Future conversion -The library is capable of converting (*futurizing*) every continuable into a fitting **std::future** through the `continuable<...>::futurize()` method. +The library is capable of converting (*futurizing*) every continuable into a fitting **std::future** through the `continuable<...>::futurize()` method.: ```c++ std::future future = http_request("github.com") @@ -243,87 +306,7 @@ std::future> future = (http_request("travis-ci.org") && http_request("atom.io")).futurize(); ``` -Continuables returning nothing will evaluate to: `std::future`. - -Continuables returning only one value will evaluate the corresponding future: `std::future`. - -Continuables returning more then one value will evaluate to a future providing a tuple carrying the values : `std::future>`. - -### In Progress (ToDo-List) - -Although the library has progressed very far there are still some candies missing: - -- [x] **Partial application**: - - We could allow callbacks to be invoked with fewer arguments than expected: - - ```C++ - http_request("github.com") - .then([]() { // ERROR: Because we expect an object accepting a std::string - // ... - }); - ``` - - -- [x] The **sequential operator** which invokes continuables sequentially and calls the callback with all results: - - ```c++ - (http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io")) - .then([](std::string github, std::string travis, std::string atom) { - // The requests are done sequentially and the callback is called - // with the response of github, travis and atom as soon as atom has responded. - // The responses of github and travis are stored meanwhile. - }); - - auto seq = cti::seq_of(http_request("github.com"), http_request("travis-ci.org")); - ``` - - This differs from the `all` connection in the way that the continuables are invoked sequentially instead of parallel. - - -- [ ] **Inplace resolution** (makes it possible to keep the nesting level flat): - - ```c++ - http_request("github.com") - .then([](std::string response) { - // Do something with the response - int response_code = get_code(response); - - return std::make_tuple(http_request("atom.io"), response_code); - // ^^^^^^^^^^^^^^^^^^^^^^^ - }) - .then([](std::string atom, int response_code) { - // - `std::string atom` was resolved from `http_request("atom.io")` - // - `response_code` was passed through the tuple directly - }); - ``` - -- [ ] Library support of **infinite logical connections**: - - ```c++ - std::vector> some; - - cti::all(std::move(some)) - .then([](std::vector result) { - // ... - }); - ``` - - This library mainly aims to support un-erased continuations, however, sometimes it's required to work with a compile-time unknown amount of continuables. - -- [ ] Maybe **Un-erasured fail/rejection handlers** and (possible exception support): - - ```c++ - http_request("github.com") - .rejected([](std::error_code) { - // Is called when the request fails - - // Potential difficult to implement with less type erasure - }); - ``` - - - +> **Note:** See the [doxygen documentation](https://naios.github.io/continuable/) for detailed information about the return type of `futurize()`. ## Compatibility @@ -335,11 +318,9 @@ Tested & compatible with: Every compiler with modern C++14 support should work. -The library only depends on the standard library when using the `continuable/continuable-base.hpp` header only which provides the full un-erasured support. +The library only depends on the standard library when using the `continuable/continuable-base.hpp` header, which provides the basic continuation logic. - - -On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)`. +> **Note:** On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)`. ## Similar implementations and alternatives @@ -351,16 +332,46 @@ There are some existing solutions with similar design thoughts already, which I Is designed in a similar way, however, it orientates itself more on the corresponding JavaScript libraries which leaves some benefits behind we could reach with modern C++ meta-programming. Like previous approaches, the library uses type erasure excessively (and thus isn't memory allocation aware). What differentiates **q** from the continuable library is that it supports infinite logical connections and ships with built-in exception support as well as it's own executors (thread pools) - thus the library isn't header-only anymore (but the library is still proclaimed to work with other executors). My personal opinion is that a continuation library shouldn't include any other stuff then the continuation logic itself. -### [cpprestsdk](https://github.com/Microsoft/cpprestsdk) +#### [cpprestsdk](https://github.com/Microsoft/cpprestsdk) Basically, the same arguments as explained in the q section apply to the cpprestsdk as well, it's major drawbacks is the overwhelming use of type-erasure. Probably you will benefit a lot from the library if you intend to use it's provided asynchronously *http*, *websocket* and *filesystem* functionalities. The *continuable* library was designed with different thoughts in mind - it basically provides the continuation logic without any support methods so you can embed it into your application without depending on a heavy framework. This makes it possible to apply continuation chaning to esoteric domains such as C++ AI scripts with fast or immediately response times. Who knows - maybe someone will provide *continuable* wrappers for open-source libraries like *asio*, libuv or *uWebSockets* in the future too. -### Others - -If I forget to mention a library here let me know, so we can add the alternatives. +> **Note:** If I forget to mention a library here let me know, so we can add the alternatives. ## License -The continuable library is licensed under the MIT License \ No newline at end of file +The continuable library is licensed under the MIT License: + +```c++ +/** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v0.8.0 + + Copyright(c) 2015 - 2017 Denis Blank + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files(the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +**/ +``` + diff --git a/examples/slideshow/example-slideshow.cpp b/examples/slideshow/example-slideshow.cpp index d4397ee..3d281d8 100644 --- a/examples/slideshow/example-slideshow.cpp +++ b/examples/slideshow/example-slideshow.cpp @@ -31,13 +31,11 @@ struct ResultSet {}; struct Buffer {}; cti::continuable mysql_query(std::string /*url*/) { - return cti::make_continuable( - [](auto&& callback) { callback("..."); }); + return cti::make_continuable([](auto&& callback) { callback(ResultSet{}); }); } cti::continuable read_file(std::string /*url*/) { - return cti::make_continuable( - [](auto&& callback) { callback("..."); }); + return cti::make_continuable([](auto&& callback) { callback(Buffer{}); }); } struct a { @@ -55,34 +53,38 @@ int main(int, char**) { // ---------- (http_request("github.com") && http_request("atom.io")) - .then([] (std::string github, std::string atom) { + .then([] (std::string /*github*/, std::string /*atom*/) { // ... return mysql_query("select * from `users`"); }) - .then([] (ResultSet result) { + .then([] (ResultSet /*result*/) { // ... }, executor->post()); // ---------- - auto cq = http_request("github.com") && http_request("atom.io") ; + auto c1 = http_request("github.com") && http_request("atom.io") ; - auto cq = http_request("github.com") || http_request("atom.io") ; + auto c2 = http_request("github.com") || http_request("atom.io") ; - auto cq = http_request("github.com") >> http_request("atom.io") ; + auto c3 = http_request("github.com") >> http_request("atom.io") ; + + (void)c1; + (void)c2; + (void)c3; // ---------- read_file("entries.csv") - .then([] (Buffer buffer) { + .then([] (Buffer /*buffer*/) { // ... return std::make_tuple("hey", true, 0); }) - .then([] (std::string msg) { + .then([] (std::string /*msg*/) { // ... }); diff --git a/test/playground/test-playground.cpp b/test/playground/test-playground.cpp index 24f15ee..bd02269 100644 --- a/test/playground/test-playground.cpp +++ b/test/playground/test-playground.cpp @@ -22,6 +22,4 @@ #include "continuable/continuable.hpp" -int main(int, char**) { - return 0; -} +int main(int, char**) { return 0; }