mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Edit the readme
This commit is contained in:
parent
7ab7c726b6
commit
ccd77dee29
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 Denis Blank
|
||||
Copyright (c) 2015 - 2017 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
227
Readme.md
227
Readme.md
@ -1,6 +1,8 @@
|
||||
# continuable->then(make_things_simple());
|
||||

|
||||
|
||||
[](https://travis-ci.org/Naios/continuable) [](https://ci.appveyor.com/project/Naios/continuable)  [](http://melpon.org/wandbox/permlink/gRWxSNHtARvRcmSY)
|
||||
 [](https://travis-ci.org/Naios/continuable) [](https://ci.appveyor.com/project/Naios/continuable)  [](https://naios.github.io/continuable/) [](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 <std::string, Buffer>,
|
||||
// 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<decltype(work)>(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 <typename... Args>
|
||||
using mycontinuation = cti::continuable_of_t<
|
||||
cti::continuable_erasure_of_t<std::function, std::function, Args...>,
|
||||
Args...>;
|
||||
using my_continuation = typename cti::continuable_trait<
|
||||
std::function, std::function, Args...
|
||||
>::continuable;
|
||||
|
||||
// ...
|
||||
|
||||
mycontinuation<int> myc = cti::make_continuable([](auto&& callback) {
|
||||
my_continuation<int> 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<std::string> future = http_request("github.com")
|
||||
@ -243,87 +306,7 @@ std::future<std::tuple<std::string, std::string>> future =
|
||||
(http_request("travis-ci.org") && http_request("atom.io")).futurize();
|
||||
```
|
||||
|
||||
Continuables returning nothing will evaluate to: `std::future<void>`.
|
||||
|
||||
Continuables returning only one value will evaluate the corresponding future: `std::future<type>`.
|
||||
|
||||
Continuables returning more then one value will evaluate to a future providing a tuple carrying the values : `std::future<std::tuple<...>>`.
|
||||
|
||||
### 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<cti::continuable<int, int>> some;
|
||||
|
||||
cti::all(std::move(some))
|
||||
.then([](std::vector<int, int> 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
|
||||
The continuable library is licensed under the MIT License:
|
||||
|
||||
```c++
|
||||
/**
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v0.8.0
|
||||
|
||||
Copyright(c) 2015 - 2017 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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.
|
||||
**/
|
||||
```
|
||||
|
||||
|
||||
@ -31,13 +31,11 @@ struct ResultSet {};
|
||||
struct Buffer {};
|
||||
|
||||
cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
|
||||
return cti::make_continuable<std::string>(
|
||||
[](auto&& callback) { callback("<html>...</html>"); });
|
||||
return cti::make_continuable([](auto&& callback) { callback(ResultSet{}); });
|
||||
}
|
||||
|
||||
cti::continuable<Buffer> read_file(std::string /*url*/) {
|
||||
return cti::make_continuable<std::string>(
|
||||
[](auto&& callback) { callback("<html>...</html>"); });
|
||||
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*/) {
|
||||
// ...
|
||||
});
|
||||
|
||||
|
||||
@ -22,6 +22,4 @@
|
||||
|
||||
#include "continuable/continuable.hpp"
|
||||
|
||||
int main(int, char**) {
|
||||
return 0;
|
||||
}
|
||||
int main(int, char**) { return 0; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user