mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Cleanup the readme
This commit is contained in:
parent
2d1fda228f
commit
27aafa2f0e
465
Readme.md
465
Readme.md
@ -1,443 +1,42 @@
|
||||

|
||||
|
||||
[](https://github.com/Naios/continuable/releases/tag/2.0.0) [](https://travis-ci.org/Naios/continuable) [](https://ci.appveyor.com/project/Naios/continuable/branch/master)  [](https://naios.github.io/continuable/) [](http://melpon.org/wandbox/permlink/xVM2szjDLEge3YLV)
|
||||
<p align="center">
|
||||
<a href="https://naios.github.io/continuable/">
|
||||
<img alt="Continuable" src="https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://naios.github.io/continuable/changelog.html#changelog-versions-3-0-0"><img alt="Current version" src="https://img.shields.io/badge/Version-3.0.0-0091EA.svg"></a>
|
||||
<a href="https://travis-ci.org/Naios/continuable"><img alt="Travic-CI build status" src="https://travis-ci.org/Naios/continuable.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/Naios/continuable/branch/master"><img alt="AppVeyor CI status" src="https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv/branch/master?svg=true"></a>
|
||||
<img alt="MIT Licensed" src="https://img.shields.io/badge/License-MIT-00838F.svg">
|
||||
<a href="https://naios.github.io/continuable/"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-Doxygen-26A69A.svg"></a>
|
||||
<a href="http://melpon.org/wandbox/permlink/xVM2szjDLEge3YLV"><img alt="Try continuable online" src="https://img.shields.io/badge/Try-online-4DB6AC.svg"></a>
|
||||
</p>
|
||||
|
||||
------
|
||||
|
||||
#### Continuable is a C++14 library that provides full support for:
|
||||
|
||||
This library provides full feature support of:
|
||||
* lazy async continuation chaining based on callbacks (**then**) and expression templates, callbacks are wrapped nicely as **promises**.
|
||||
* **no enforced type-erasure** which means we need **less heap allocations** than comparable libraries, strictly following the **"don't pay for what you don't use"** principle.
|
||||
* support for *all*, *any* and *sequential* connections between continuables through expressive operator overloads **&&**, **||** and **>>** as well as free functions **when_all**, **when_any** and **when_seq**.
|
||||
* asynchronous **error handling** through **exceptions**, **error codes** and **user defined types**.
|
||||
* syntactic sugar for instance: **partial invocation**, **tuple unpacking**, `co_await` support and **executors**.
|
||||
* **encapsuled from any runtime**, larger framework or executors makes it possible to use continuable even in smaller or esoteric usage scenarios.
|
||||
|
||||
* lazy async continuation chaining based on **callbacks** (*then*) and expression templates, callbacks are wrapped nicely as promises.
|
||||
* **no enforced type-erasure** which means we need **less heap allocations**, strictly following the **"don't pay for what you don't use"** principle.
|
||||
* support for **connections** between continuables through an **all, any or sequential** strategy through expressive operator overloads **&&**, **||** and **>>**.
|
||||
* **error handling** through exceptions or custom types.
|
||||
* **syntactic sugar** for instance: **partial invocation**, **tuple unpacking** and **executors**.
|
||||
------
|
||||
|
||||
#### Getting started:
|
||||
|
||||
The [documentation](https://naios.github.io/continuable/) offers everything you need:
|
||||
* [Installation guide](https://naios.github.io/continuable/installation.html)
|
||||
* [Usage tutorial](https://naios.github.io/continuable/tutorial.html)
|
||||
* [Configuration explanation](https://naios.github.io/continuable/configuration.html)
|
||||
* [Changelog](https://naios.github.io/continuable/changelog.html)
|
||||
|
||||
## The basics
|
||||
|
||||
|
||||
* Supply a continuable which is invoked lazily upon request:
|
||||
```cpp
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>([url = std::move(url)](auto&& promise) {
|
||||
// Perform the actual request through a different library,
|
||||
// resolve the promise upon completion of the task.
|
||||
promise.set_value("<html> ... </html>");
|
||||
|
||||
// Or promise.set_exception(...);
|
||||
});
|
||||
}
|
||||
```
|
||||
* Continue the continuation using `.then(...)` and `.fail(...)`, exceptions are passed to the first available handler:
|
||||
|
||||
```cpp
|
||||
http_request("github.com")
|
||||
.then([] (std::string result) {
|
||||
// Do something...
|
||||
return http_request("travis-ci.org")
|
||||
})
|
||||
.then([] (std::string result) {
|
||||
// Handle the response from 'travis-ci.org'
|
||||
})
|
||||
.fail([] (std::exception_ptr ptr) {
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception or error code here
|
||||
}
|
||||
});
|
||||
```
|
||||
* Create connections between the continuables and use its compound result:
|
||||
```cpp
|
||||
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
|
||||
.then([](std::string github, std::string travis_or_atom) {
|
||||
// The promise is called with the response of github and travis or atom.
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [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)
|
||||
- [Error handling](#error-handling)
|
||||
- [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)
|
||||
- [Coroutines](#coroutines)
|
||||
- [Future conversion](#future-conversion)
|
||||
- [Compatibility](#compatibility)
|
||||
- [License](#license)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### How-to use
|
||||
|
||||
As mentioned earlier the library is header-only. There is a cmake project provided for simple setup:
|
||||
|
||||
#### As git submodule
|
||||
|
||||
```sh
|
||||
# Shell:
|
||||
git submodule add https://github.com/Naios/continuable.git
|
||||
```
|
||||
|
||||
```cmake
|
||||
# CMake file:
|
||||
add_subdirectory(continuable)
|
||||
# continuable provides an interface target which makes it's
|
||||
# headers available to all projects using the continuable library.
|
||||
target_link_libraries(my_project continuable)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
#### As CMake library
|
||||
|
||||
Additionally the project exports a `continuable` target which is importable through CMake when installed:
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --target INSTALL --config Release
|
||||
```
|
||||
|
||||
`CMakeLists.txt`:
|
||||
|
||||
```cmake
|
||||
find_package(continuable REQUIRED)
|
||||
```
|
||||
|
||||
### Building the unit-tests
|
||||
|
||||
In order to build the unit tests clone the repository recursively with all submodules:
|
||||
|
||||
```sh
|
||||
# Shell:
|
||||
git clone --recursive https://github.com/Naios/continuable.git
|
||||
```
|
||||
## Stability and version
|
||||
|
||||
The library follows the rules of [semantic versioning](http://semver.org/), the API is kept stable across minor versions.
|
||||
|
||||
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 promise taking function:
|
||||
|
||||
```c++
|
||||
#include <continuable/continuable-base.hpp>
|
||||
// ...
|
||||
|
||||
auto void_continuable = cti::make_continuable<void>([](auto&& promise) {
|
||||
// ^^^^
|
||||
|
||||
// Resolve the promise later when you have finished your work
|
||||
promise.set_value();
|
||||
});
|
||||
|
||||
auto str_continuable = cti::make_continuable<std::string>([](auto&& promise) {
|
||||
// ^^^^^^^^^^^
|
||||
promise.set_value("Hello, World!");
|
||||
});
|
||||
```
|
||||
|
||||
### Chaining Continuables
|
||||
|
||||
Chain continuables together in order to build up an asynchronous call hierarchy:
|
||||
|
||||
```c++
|
||||
mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
.then([](ResultSet users) {
|
||||
// Return the next continuable to process ...
|
||||
return mysql_query("SELECT `id` name FROM `sessions`");
|
||||
})
|
||||
.then([](ResultSet sessions) {
|
||||
// ... or pass multiple values to the next callback using tuples or pairs ...
|
||||
return std::make_tuple(std::move(sessions), true);
|
||||
})
|
||||
.then([](ResultSet sessions, bool is_ok) {
|
||||
// ... or pass a single value to the next callback ...
|
||||
return 10;
|
||||
})
|
||||
.then([](auto value) {
|
||||
// ^^^^ Templated callbacks are possible too
|
||||
})
|
||||
// ... you may even pass continuables to the `then` method directly:
|
||||
.then(mysql_query("SELECT * `statistics`"))
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** The continuation chain is invoked when the object is destructed or the `done()` method is called.
|
||||
|
||||
### Providing helper functions
|
||||
|
||||
Returning continuables from helper functions makes chaining simple and expressive:
|
||||
|
||||
```c++
|
||||
#include <continuable/continuable-base.hpp>
|
||||
// ...
|
||||
|
||||
auto mysql_query(std::string query) {
|
||||
return cti::make_continuable<ResultSet>([query = std::move(query)](auto&& promise) mutable {
|
||||
// Pass the callback to the handler which calls the callback when finished.
|
||||
// Every function accepting callbacks works with continuables.
|
||||
mysql_handle_async_query(std::move(query),
|
||||
std::forward<decltype(promise)>(promise));
|
||||
});
|
||||
}
|
||||
|
||||
// You may use the helper function like you would normally do,
|
||||
// without using the support methods of the continuable.
|
||||
mysql_query("DELETE FROM `users` WHERE `id` = 27361");
|
||||
|
||||
// Or using chaining to handle the result which is covered in the documentation.
|
||||
mysql_query("SELECT `id`, `name` FROM users")
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Error handling
|
||||
|
||||
Continuables support asynchronous error handling through exceptions or custom error types.
|
||||
|
||||
The error type will be **`std::exception_ptr`** except if one of the following definition is defined:
|
||||
- **`CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`**: Define this to use a user defined error type.
|
||||
- **`CONTINUABLE_WITH_NO_EXCEPTIONS`**: Define this to use **`std::error_condition`** as error type and to disable exception support. When exceptions are disabled this definition is set automatically.
|
||||
|
||||
Resolving a promise through an error will skip all following result handlers attached through **`then`**:
|
||||
|
||||
```cpp
|
||||
auto get_bad_continuable(std::exception const& e) {
|
||||
return cti::make_continuable<void>([=] (auto&& promise) {
|
||||
try {
|
||||
throw e;
|
||||
} catch(...) {
|
||||
promise.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
You may handle the exception as following:
|
||||
|
||||
```cpp
|
||||
get_bad_continuable()
|
||||
.then([] {
|
||||
// ... never invoked
|
||||
})
|
||||
.then([] {
|
||||
// ... never invoked as well
|
||||
})
|
||||
.fail([] (std::exception_ptr e) {
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception here
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 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, 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) {
|
||||
return cti::make_continuable<std::string>([](auto&& promise) {
|
||||
promise.set_value("<html>...</html>");
|
||||
});
|
||||
}
|
||||
|
||||
// `all` 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 callback is called with the response of github, travis and atom.
|
||||
});
|
||||
|
||||
// `any` of connections:
|
||||
(http_request("github.com") || http_request("travis-ci.org") || http_request("atom.io"))
|
||||
.then([](std::string github_or_travis_or_atom) {
|
||||
// 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) {
|
||||
// The callback is called with the response of github for sure
|
||||
// and the second parameter represents the response of travis or atom.
|
||||
});
|
||||
|
||||
// There are helper functions for connecting continuables:
|
||||
auto all = cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto any = cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto seq = cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
|
||||
```
|
||||
|
||||
> **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
|
||||
|
||||
The library was designed in order to avoid type-erasure until really needed. Thus we provide traits to create an alias to a continuable using the **type-erasure backend of your choice**. All templated functors providing a call operator may be used as a backend (*std::function* for instance).
|
||||
|
||||
The library provides aliases for using my [function2 library](https://github.com/Naios/function2) as backend which provides efficient and qualifier correct function wrappers for copyable and non-copyable objects.
|
||||
|
||||
```c++
|
||||
#include <continuable/continuable.hpp>
|
||||
|
||||
cti::unique_continuable<int, std::string> unique =
|
||||
cti::make_continuable([value = std::make_unique<int>(0)](auto&& promise) {
|
||||
|
||||
// The use of non copyable objects is possible by design if
|
||||
// the type erasure backend supports it.
|
||||
promise.set_value(*value, "Hello, World!");
|
||||
});
|
||||
|
||||
std::move(unique).then([](int i) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
We could also think about using `std::future` as backend but this is even worse then using `std::function` because usually there is, even more, type erasure and allocations involved.
|
||||
Additionally `std::function` doesn't provide support for multiple function overloads
|
||||
|
||||
### Coroutines
|
||||
|
||||
Since version 2.0.0 coroutines (`co_await` and `co_return`) are supported by continuables when the underlying toolchain supports the TS. Currently this works in MSVC 2017 and Clang 5.0.
|
||||
You have to enable this capability through the `CTI_CONTINUABLE_WITH_AWAIT` define in CMake.
|
||||
|
||||
```c++
|
||||
int i = co_await cti::make_continuable<int>([](auto&& promise) {
|
||||
promise.set_value(0);
|
||||
});
|
||||
```
|
||||
|
||||
### Future conversion
|
||||
|
||||
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")
|
||||
.then([](std::string response) {
|
||||
// Do sth...
|
||||
return http_request("travis-ci.org") || http_request("atom.io");
|
||||
})
|
||||
.apply(cti::transforms::futurize());
|
||||
// ^^^^^^^^
|
||||
|
||||
std::future<std::tuple<std::string, std::string>> future =
|
||||
(http_request("travis-ci.org") && http_request("atom.io"))
|
||||
.apply(cti::transforms::futurize());
|
||||
```
|
||||
|
||||
> **Note:** See the [doxygen documentation](https://naios.github.io/continuable/) for detailed information about the return type of `futurize()`.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Tested & compatible with:
|
||||
|
||||
- Visual Studio 2017+ Update 2
|
||||
- Clang 5.0+
|
||||
- GCC 6.0+
|
||||
|
||||
Although the build is observed with the latest toolchains earlier ones might work.
|
||||
|
||||
The library only depends on the standard library when using the `continuable/continuable-base.hpp` header, which provides the basic continuation logic.
|
||||
|
||||
> **Note:** On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)` when using future based transforms.
|
||||
|
||||
## License
|
||||
|
||||
The continuable library is licensed under the MIT License:
|
||||
|
||||
```
|
||||
/**
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v2.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 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.
|
||||
**/
|
||||
```
|
||||
#### Issues and contributions
|
||||
|
||||
Issues reports are accepted through the Github issue tracker as well as Pull requests.
|
||||
Every contribution is welcome! Don't hesitate to ask for help if you need any support
|
||||
in changing the implementation or when using the library.
|
||||
|
||||
@ -38,12 +38,12 @@ namespace cti {
|
||||
\link continuable_base::operator>> >>\endlink as well as free functions
|
||||
\ref when_all, \ref when_any and \ref when_seq.
|
||||
- asynchronous \link continuable_base::fail error handling\endlink through
|
||||
\link promise_base_base::set_exception exceptions\endlink,
|
||||
\link promise_base::set_exception exceptions\endlink,
|
||||
\link configuration error codes\endlink and
|
||||
\link configuration user defined types\endlink.
|
||||
- **syntactic sugar** for instance: **partial invocation**, **tuple unpacking**
|
||||
and \link continuable_base::then executors\endlink.
|
||||
- **encapsuled from any runtime**, larger framework or executor making
|
||||
- **syntactic sugar** for instance: **partial invocation**, **tuple unpacking**,
|
||||
`co_await` support and \link continuable_base::then executors\endlink.
|
||||
- **encapsuled from any runtime**, larger framework or executors makes
|
||||
it possible to use continuable even in smaller or esoteric usage scenarios.
|
||||
|
||||
\section mainpage-getting-started Getting started
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user