initial commit

This commit is contained in:
Denis Blank 2017-02-26 00:33:32 +01:00
commit 6e5ec79754
34 changed files with 3003 additions and 0 deletions

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
BasedOnStyle: LLVM
PointerAlignment: Left
IndentCaseLabels: true

54
.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
# 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/
format.sh
push.sh
commit.sh
pull.sh

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "dep/googletest/googletest"]
path = dep/googletest/googletest
url = https://github.com/google/googletest.git
[submodule "dep/function2/function2"]
path = dep/function2/function2
url = https://github.com/Naios/function2.git

104
.travis.yml Normal file
View File

@ -0,0 +1,104 @@
sudo: true
dist: trusty
language: cpp
git:
depth: 1
matrix:
include:
- os: linux
compiler: gcc
addons:
apt:
packages:
- valgrind
- g++-5
sources:
- ubuntu-toolchain-r-test
env:
- COMPILER=g++-5
- os: linux
compiler: gcc
addons:
apt:
packages:
- valgrind
- g++-6
sources:
- ubuntu-toolchain-r-test
env:
- COMPILER=g++-6
- os: linux
compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.7 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
packages:
- g++-6
- clang-3.7
env:
- COMPILER=clang++-3.7
- os: linux
compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
packages:
- g++-6
- clang-3.9
env:
- COMPILER=clang++-3.9
install:
- export CXX=$COMPILER
- $CXX --version
# Function for creating a new 'build' directory
- |
function invoke_build {
echo "Renew build directory..."
cd $TRAVIS_BUILD_DIR
# Remove any existing build directory
[ -e build ] && rm -r -f build
mkdir build
cd build
# Configure the project and build it
cmake -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS -Werror" -DCMAKE_BUILD_TYPE=Debug ..
make -j2
}
script:
- |
if [[ $COMPILER == *"clang"* ]]; then
# Build the test suite with various sanitizers:
# - ASan (LSan):
echo "Building with address sanitizer..."
CMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer"
invoke_build
ctest --verbose
# - UBSan:
echo "Building with undefined behaviour sanitizer..."
CMAKE_CXX_FLAGS="-fsanitize=undefined -fno-omit-frame-pointer"
invoke_build
ctest --verbose
else
# Build an run the tests suite with valgrind
invoke_build
valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes ctest --verbose
fi
notifications:
email: false

52
CMakeLists.txt Normal file
View File

@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.2)
project(continuable C CXX)
# Dependencies
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# Continuable
add_library(continuable INTERFACE)
target_include_directories(continuable
INTERFACE
"${CMAKE_CURRENT_LIST_DIR}/include")
target_link_libraries(continuable
INTERFACE
function2
Threads::Threads)
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
cxx_return_type_deduction)
# 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(examples)
add_subdirectory(test)
endif ()

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
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.

366
Readme.md Normal file
View File

@ -0,0 +1,366 @@
# continuable->then(make_things_simple());
[![Build Status](https://travis-ci.org/Naios/continuable.svg?branch=master)](https://travis-ci.org/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/TPSde7EsCcXcC72D)
> Async C++14 platform independent continuation chainer providing light and allocation aware futures
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.
## The library design
The continuable library was designed in order to provide you as much as flexibility as possible:
- There is no enforced type erasure which means there is less memory allocation and thus the callback chains are heavily optimizable by the compiler. That's why the library is well usable in the embedded or gaming field. **Don't pay for what you don't use!**
- The library provides support for **dispatching callbacks on a given executor**, however, it doesn't provide it's own one. You probably will use your own executor like [asio](https://github.com/chriskohlhoff/asio), [libuv](https://github.com/libuv/libuv) or a corresponding [lock-free concurrentqueue](https://github.com/cameron314/concurrentqueue) anyway. In most cases, the executor will do the type erasure for you, so there is no reason to do it twice.
- The library provides as much as **syntactic sugar** as it's possible, in order to make continuation chaining expressive and simple. For instance, it allows you to logical connect continuables through the well-known operators `&&` and `||`.
- The library is header only and has **fewer dependencies**:
- The `continuable-base.hpp` header only depends on the standard library and provides the basic continuation logic.
- The `continuable-test.hpp` header also depends on `gtest` because it adds various test macros for asserting on the result of asynchronous continuations.
- The `continuable.hpp`header depends on my header-only [function2](https://github.com/Naios/function2) library for providing efficient type erasure - non-copyable objects are natively supported without any workaround.
## Installation
### Inclusion
As mentioned earlier the library is header-only. There is a cmake project provided for simple setup:
```sh
# Shell:
git submodule add https://github.com/Naios/continuable.git
```
```cma
# 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 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
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
Currently, the library is in the incubation state, it provides a stable functionality as the CI unit tests indicate.
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).
## Quick reference
### Creating Continuables
Create a continuable from a callback taking function:
```c++
#include "continuable/continuable-base.hpp"
// ...
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
// ^^^^
// Call the callback later when you have finished your work
callback();
});
auto str_continuable = cti::make_continuable<std::string>([](auto&& callback) {
// ^^^^^^^^^^^
callback("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) {
// ...
});
```
### 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&& callback) 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(callback)>(callback));
});
}
// 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) {
// ...
});
```
### Connecting Continuables {all or any}
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.
```C++
auto http_request(std::string url) {
return cti::make_continuable<std::string>([](auto&& callback) {
callback("<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.
});
// 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::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"));
```
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).
### 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&& callback) {
// The use of non copyable objects is possible by design if
// the type erasure backend supports it.
callback(*value, "Hello, World!");
});
std::move(unique).then([](int i) {
// ...
});
```
However you may still define your own continuation wrapper with the backend of your choice, but keep in mind that the capabilities of your wrapper determine the possible objects, the continuation is capable of carrying. This limits continuations using *std::function* as a backend to copyable types:
```c++
template <typename... Args>
using mycontinuation = cti::continuable_of_t<
cti::continuable_erasure_of_t<std::function, std::function, Args...>,
Args...>;
// ...
mycontinuation<int> myc = cti::make_continuable([](auto&& callback) {
// ^^^^^
// Signatures may be omitted for continuables which are type erased
callback(0);
});
```
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.
### 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");
})
.futurize();
// ^^^^^^^^
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:
- [ ] **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
// ...
});
```
- [ ] The **sequential/pipe 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
});
```
## Compatibility
Tested & compatible with:
- Visual Studio 2015+ Update 3
- Clang 3.6+
- GCC 5.0+
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.
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
You already thought it, the idea isn't new and thus it is well known in the JavaScript world already.
There are some existing solutions with similar design thoughts already, which I don't want to hold back from you - you should choose the library fitting your needs best:
#### **[q (C++)](https://github.com/grantila/q)**
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)
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.
## License
The continuable library is licensed under the MIT License

1
cmake/CMakeLists.txt Normal file
View File

@ -0,0 +1 @@
include(cmake/configure_compiler.cmake)

View File

@ -0,0 +1,8 @@
# Enable full warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")
if (TESTS_NO_EXCEPTIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
message(STATUS "Clang: Disabled exceptions")
endif()

7
cmake/compiler/gcc.cmake Normal file
View File

@ -0,0 +1,7 @@
# Enable full warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")
if (TESTS_NO_EXCEPTIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
message(STATUS "GCC: Disabled exceptions")
endif()

25
cmake/compiler/msvc.cmake Normal file
View File

@ -0,0 +1,25 @@
if (${MSVC_VERSION} LESS 1900)
message(FATAL_ERROR "You are using an unsupported version of Visual Studio "
"which doesn't support all required C++11 features. "
"(Visual Studio 2015 (version >= 1900) is required!)")
endif()
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")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
if (TESTS_NO_EXCEPTIONS)
add_definitions(-D_HAS_EXCEPTIONS=0)
string(REGEX REPLACE "/GX" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
message(STATUS "MSVC: Disabled exceptions")
endif()

View File

@ -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()

2
dep/CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
add_subdirectory(googletest)
add_subdirectory(function2)

View File

@ -0,0 +1 @@
add_subdirectory(function2)

@ -0,0 +1 @@
Subproject commit dfb91a53cbac35ac4800aac058e0905f0384a431

View File

@ -0,0 +1,44 @@
add_library(gtest STATIC
${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc)
target_compile_features(gtest
PUBLIC
cxx_alias_templates
cxx_auto_type
cxx_decltype
cxx_final
cxx_lambdas
cxx_variadic_templates
cxx_defaulted_functions
cxx_nullptr)
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)
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)

@ -0,0 +1 @@
Subproject commit 51143d5b62521f71020ada4ba1b6b44f3a6749bb

1
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1 @@
add_subdirectory(documentation)

View File

@ -0,0 +1,5 @@
add_executable(example-documentation
${CMAKE_CURRENT_LIST_DIR}/example-documentation.cpp)
target_link_libraries(example-documentation
PRIVATE
continuable)

View File

@ -0,0 +1,145 @@
/**
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.
**/
#include "continuable/continuable.hpp"
using cti::detail::util::unused;
void creating_continuables() {
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
// ^^^^
// Call the callback later when you have finished your work
callback();
});
auto str_continuable =
cti::make_continuable<std::string>([](auto&& callback) {
// ^^^^^^^^^^^
callback("Hello, World!");
});
}
struct ResultSet {};
template <typename... Args> void mysql_handle_async_query(Args&&...) {}
auto mysql_query(std::string query) {
return cti::make_continuable<ResultSet>([query = std::move(query)](
auto&& callback) 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(callback)>(callback));
});
}
void providing_helper_functions() {
// 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) {
// ...
unused(result);
});
}
void chaining_continuables() {
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](ResultSet users) {
(void)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) {
(void)sessions;
(void)is_ok;
// ... or pass a single value to the next callback ...
return 10;
})
.then([](auto value) {
// ^^^^ Templated callbacks are possible too
(void)value;
})
// ... you may even pass continuables to the `then` method directly:
.then(mysql_query("SELECT * `statistics`"))
.then([](ResultSet result) {
// ...
(void)result;
});
}
auto http_request(std::string /*url*/) {
return cti::make_continuable<std::string>(
[](auto&& callback) { callback("<html>...</html>"); });
}
void connecting_continuables() {
// `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.
unused(github, travis, 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.
unused(github_or_travis_or_atom);
});
// 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.
unused(github, travis_or_atom);
});
// 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"));
}
int main() {
creating_continuables();
providing_helper_functions();
chaining_continuables();
connecting_continuables();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,145 @@
/**
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.
**/
#ifndef CONTINUABLE_TESTING_HPP_INCLUDED__
#define CONTINUABLE_TESTING_HPP_INCLUDED__
#include "gtest/gtest.h"
#include "continuable/continuable-base.hpp"
namespace cti {
inline namespace abi_v1 {
namespace detail {
namespace testing {
template <typename C> void expect_async_completion(C&& continuable) {
bool called = false;
std::forward<C>(continuable).then([&](auto&&... args) {
ASSERT_FALSE(called);
called = true;
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
});
EXPECT_TRUE(called);
}
template <typename C, typename V>
void expect_async_validation(C&& continuable, V&& validator) {
expect_async_completion(
std::forward<C>(continuable)
.then([validator =
std::forward<V>(validator)](auto&&... args) mutable {
validator(std::forward<decltype(args)>(args)...);
}));
}
/// Expects that the continuable is finished with the given arguments
template <typename C, typename V, typename... Args>
void expect_async_binary_validation(C&& continuable, V&& validator,
Args&&... expected) {
expect_async_validation(std::forward<C>(continuable), [
expected_pack = std::make_tuple(std::forward<Args>(expected)...),
validator = std::forward<V>(validator)
](auto&&... args) mutable {
auto actual_pack = std::make_tuple(std::forward<decltype(args)>(args)...);
auto size = util::pack_size_of(util::identity_of(expected_pack));
static_assert(size.value == std::tuple_size<decltype(actual_pack)>::value,
"Async completion handler called with a different count "
"of arguments!");
util::static_for_each_in(
std::make_index_sequence<size.value>{}, [&](auto current) mutable {
auto expected = std::get<current.value>(std::move(expected_pack));
auto actual = std::get<current.value>(std::move(actual_pack));
(void)current;
validator(expected, actual);
});
});
}
template <typename C, typename... Args>
void expect_async_result(C&& continuable, Args&&... expected) {
expect_async_binary_validation(
std::forward<C>(continuable),
[](auto&& expected, auto&& actual) { EXPECT_EQ(expected, actual); },
std::forward<Args>(expected)...);
}
template <typename C, typename... Args>
void expect_async_types(C&& continuable, util::identity<Args...> expected) {
expect_async_validation(
std::forward<C>(continuable), [&](auto... actualPack) {
auto actual = util::identity<decltype(actualPack)...>{};
util::unused(expected, actual,
std::forward<decltype(actualPack)>(actualPack)...);
static_assert(
std::is_same<std::decay_t<decltype(expected)>,
std::decay_t<decltype(actual)>>::value,
"The called arguments don't match with the expected ones!");
});
}
} // end namespace testing
} // end namespace detail
} // end inline namespace abi_...
} // end namespace cti
/// Expects the final callback of the given continuable to be called
/// with any result.
#define EXPECT_ASYNC_COMPLETION(CONTINUABLE) \
cti::detail::testing::expect_async_completion(CONTINUABLE);
/// Expects the continuation to be called and forwards it's arguments to
/// the given validator which can then do assertions on the result.
#define EXPECT_ASYNC_VALIDATION(CONTINUABLE, VALIDATOR) \
cti::detail::testing::expect_async_validation(CONTINUABLE, VALIDATOR);
/// Expects the continuation to be called and forwards it's arguments to
/// the given validator which can then do assertions on the result:
///
/// auto validator = [](auto expected, auto actual) {
/// // ...
/// };
///
/// The validator is called for every expecting and actual result.
#define EXPECT_ASYNC_BINARY_VALIDATION(CONTINUABLE, ...) \
cti::detail::testing::expect_async_binary_validation(CONTINUABLE, \
__VA_ARGS__);
/// Expects that the continuable is finished with the given result
#define EXPECT_ASYNC_RESULT(...) \
cti::detail::testing::expect_async_result(__VA_ARGS__);
/// Expects that the continuable is finished with the given type of arguments
/// without validating it against equality.
#define EXPECT_ASYNC_TYPES(CONTINUABLE, ...) \
cti::detail::testing::expect_async_types( \
CONTINUABLE, cti::detail::util::identity<__VA_ARGS__>{})
#endif // CONTINUABLE_TESTING_HPP_INCLUDED__

View File

@ -0,0 +1,53 @@
/**
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.
**/
#ifndef CONTINUABLE_HPP_INCLUDED__
#define CONTINUABLE_HPP_INCLUDED__
#include <type_traits>
#include "continuable/continuable-base.hpp"
#include "function2/function2.hpp"
namespace cti {
/// Defines a copyable continuation type which uses the
/// function2 backend for type erasure.
///
/// Usable like: continuable<int, float>
template <typename... Args>
using continuable = continuable_of_t<
continuable_erasure_of_t<fu2::function, fu2::unique_function, Args...>,
Args...>;
/// Defines a non-copyable continuation type which uses the
/// function2 backend for type erasure.
///
/// Usable like: unique_continuable<int, float>
template <typename... Args>
using unique_continuable =
continuable_of_t<continuable_erasure_of_t<fu2::unique_function,
fu2::unique_function, Args...>,
Args...>;
} // end namespace cti
#endif // CONTINUABLE_HPP_INCLUDED__

3
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,3 @@
add_subdirectory(playground)
add_subdirectory(threads)
add_subdirectory(unit-test)

View File

@ -0,0 +1,6 @@
add_executable(test-playground
${CMAKE_CURRENT_LIST_DIR}/test-playground.cpp)
target_link_libraries(test-playground
PRIVATE
continuable)

View File

@ -0,0 +1,37 @@
/**
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.
**/
#include "continuable/continuable.hpp"
auto invoke() {
return cti::make_continuable<void>([](auto&& callback) { callback(); });
}
void trythestuff() {
auto future = invoke().futurize();
future.get();
}
int main(int, char**) {
trythestuff();
return 0;
}

View File

@ -0,0 +1,9 @@
add_executable(test-threads
${CMAKE_CURRENT_LIST_DIR}/test-threads.cpp)
target_link_libraries(test-threads
PRIVATE
continuable)
add_test(NAME continuable-threads-tests
COMMAND test-threads)

View File

@ -0,0 +1,38 @@
/**
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.
**/
/// This test ensures that we linked against -lpthread correctly
#include <future>
void resolve(std::promise<int> p) { p.set_value(0); }
int main(int, char**) {
std::promise<int> p;
auto f = p.get_future();
resolve(std::move(p));
return f.get();
}

View File

@ -0,0 +1,17 @@
add_executable(test-continuable
${CMAKE_SOURCE_DIR}/include/continuable/continuable.hpp
${CMAKE_SOURCE_DIR}/include/continuable/continuable-base.hpp
${CMAKE_SOURCE_DIR}/include/continuable/continuable-testing.hpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable.hpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-base.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-connection.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-transforms.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-recursion.cpp)
target_link_libraries(test-continuable
PRIVATE
gtest-main
continuable)
add_test(NAME continuable-unit-tests
COMMAND test-continuable)

View File

@ -0,0 +1,129 @@
/**
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.
**/
#include "test-continuable.hpp"
using namespace cti;
using namespace cti::detail;
TYPED_TEST(single_dimension_tests, are_supplyd_on_destruct) {
{
auto allowed = false;
// Are not supplyd until destruction
auto continuable = this->supply().then([&] { ASSERT_TRUE(allowed); });
ASSERT_FALSE(allowed);
allowed = true;
EXPECT_ASYNC_COMPLETION(std::move(continuable));
}
EXPECT_ASYNC_RESULT(this->supply());
EXPECT_ASYNC_RESULT(this->supply(0xDA), 0xDA);
EXPECT_ASYNC_TYPES(this->supply(tag1{}), tag1);
}
TYPED_TEST(single_dimension_tests, are_chainable) {
EXPECT_ASYNC_RESULT(this->supply().then([] {
return; // void
}));
// Type chain
{
auto chain = this->supply().then([] { return tag1{}; });
EXPECT_ASYNC_TYPES(std::move(chain), tag1);
}
// Pair chain
{
auto chain = this->supply().then([] {
// ...
return std::make_pair(tag1{}, tag2{});
});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2);
}
// Tuple chain
{
auto chain = this->supply().then([] {
// ...
return std::make_tuple(tag1{}, tag2{}, tag3{});
});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
}
// Erasing chain
{
auto chain = this->supply().then(this->supply(tag1{}));
EXPECT_ASYNC_TYPES(std::move(chain), tag1);
}
// Continuing chain
{
auto chain = this->supply().then([&] { return this->supply(tag1{}); });
EXPECT_ASYNC_TYPES(std::move(chain), tag1);
}
}
TYPED_TEST(single_dimension_tests, are_executable_through_dispatchers) {
auto is_ready = [](auto& future) {
// Check that the future is ready
return future.wait_for(std::chrono::seconds(0)) ==
std::future_status::ready;
};
{
auto future = this->supply().futurize();
ASSERT_TRUE(is_ready(future));
future.get();
}
{
auto future = this->supply()
.then([] {
// ...
return 0xFD;
})
.futurize();
ASSERT_TRUE(is_ready(future));
EXPECT_EQ(future.get(), 0xFD);
}
{
auto canary = std::make_tuple(0xFD, 0xF5);
auto future = this->supply()
.then([&] {
// ...
return canary;
})
.futurize();
ASSERT_TRUE(is_ready(future));
EXPECT_EQ(future.get(), canary);
}
}

View File

@ -0,0 +1,82 @@
/**
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.
**/
#include "test-continuable.hpp"
TYPED_TEST(single_dimension_tests, is_logical_and_connectable) {
{
auto chain = this->supply() && this->supply();
EXPECT_ASYNC_RESULT(std::move(chain));
}
{
auto chain = this->supply(1) && this->supply(2);
EXPECT_ASYNC_RESULT(std::move(chain), 1, 2);
}
{
auto chain = this->supply(1, 2) && this->supply(3, 4, 5);
EXPECT_ASYNC_RESULT(std::move(chain), 1, 2, 3, 4, 5);
}
{
auto chain = this->supply(tag1{}) && this->supply(tag2{}, tag3{});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
}
}
TYPED_TEST(single_dimension_tests, is_logical_or_connectable) {
{
auto chain = this->supply() || this->supply();
EXPECT_ASYNC_RESULT(std::move(chain));
}
{
auto chain = this->supply(1) || this->supply(2);
EXPECT_ASYNC_RESULT(std::move(chain), 1);
}
{
auto chain = this->supply(1, 2) || this->supply(3, 4);
EXPECT_ASYNC_RESULT(std::move(chain), 1, 2);
}
{
auto chain = this->supply(tag1{}, tag2{}) || this->supply(tag1{}, tag2{});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2);
}
{
auto chain = this->supply(tag1{}, tag2{}, tag3{}) ||
this->supply(tag1{}, tag2{}, tag3{});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
}
{
using common = std::common_type_t<char, int>;
auto chain = this->supply(char(0), int(0)) || this->supply(int(0), char(0));
EXPECT_ASYNC_TYPES(std::move(chain), common, common);
}
}

View File

@ -0,0 +1,49 @@
/**
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.
**/
#include "test-continuable.hpp"
#include <functional>
using namespace cti;
using namespace cti::detail;
TEST(recursion_tests, are_multiple_args_mergeable) {
{
auto tp = std::make_tuple(1, 2, 3);
util::merge(tp, tp, tp, tp, tp);
}
auto tp2 =
util::merge(std::make_tuple(), std::make_tuple(1), std::make_tuple(1, 2),
std::make_tuple(1, 2, 3), std::make_tuple(1, 2, 3, 4));
auto count = util::unpack(
tp2, [](auto... args) { return util::fold(std::plus<int>{}, args...); });
EXPECT_EQ(count, 20);
}
TEST(recursion_tests, are_noncopyable_mergeable) {
std::tuple<util::non_copyable> nc1, nc2, nc3;
util::merge(std::move(nc1), std::move(nc2), std::move(nc3));
}

View File

@ -0,0 +1,34 @@
/**
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.
**/
#include "test-continuable.hpp"
using namespace cti;
TEST(ContinuableErasureTests, FunctionsAreUnwrappable) {
// ...
// continuable<int> ss = supply(0);
// auto itm = std::move(ss).then(supply(2));
}

View File

@ -0,0 +1,160 @@
/**
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.
**/
#ifndef TEST_CONTINUABLE_HPP__
#define TEST_CONTINUABLE_HPP__
#include <cassert>
#include "continuable/continuable-base.hpp"
#include "continuable/continuable-testing.hpp"
#include "continuable/continuable.hpp"
#include "gtest/gtest.h"
template <typename Provider>
class continuation_provider : public ::testing::Test, public Provider {
public:
auto supply() {
return this->makeVoid([](auto&& callback) mutable {
// ...
std::forward<decltype(callback)>(callback)();
});
};
template <typename... Args> auto supply(Args&&... args) {
return this->template make<std::decay_t<Args>...>([values = std::make_tuple(
std::forward<Args>(
args)...)](
auto&& callback) mutable {
cti::detail::util::unpack(std::move(values), [&](auto&&... passed) {
// ...
std::forward<decltype(callback)>(callback)(
std::forward<decltype(passed)>(passed)...);
});
});
}
};
inline auto empty_caller() {
return [](auto&& callback) {
// ...
std::forward<decltype(callback)>(callback)();
};
}
struct provide_copyable {
template <typename T> auto makeVoid(T&& callback) {
return make<void>(std::forward<T>(callback));
}
template <typename... Args, typename T> auto make(T&& callback) {
return cti::make_continuable<Args...>(std::forward<T>(callback));
}
};
struct provide_unique {
template <typename T> auto makeVoid(T&& callback) {
return make<void>(std::forward<T>(callback));
}
template <typename... Args, typename T> auto make(T&& callback) {
return cti::make_continuable<Args...>([
callback = std::forward<T>(callback), guard = std::make_unique<int>(0)
](auto&&... args) mutable {
(void)(*guard);
return std::move(callback)(std::forward<decltype(args)>(args)...);
});
}
};
struct provide_copyable_erasure {
template <typename T> auto makeVoid(T&& callback) {
return make(std::forward<T>(callback));
}
template <typename... Args, typename T>
cti::continuable<Args...> make(T&& callback) {
return cti::make_continuable(std::forward<T>(callback));
}
};
struct provide_unique_erasure {
template <typename T> auto makeVoid(T&& callback) {
return make(std::forward<T>(callback));
}
template <typename... Args, typename T>
cti::unique_continuable<Args...> make(T&& callback) {
return cti::make_continuable(std::forward<T>(callback));
}
};
/*
template <typename Left, typename Right> struct provide_continuation_or_left {
Left left_;
Right right_;
template <typename T> auto makeVoid(T&& callback) {
return left_.template make<void>(std::forward<T>(callback)) ||
right_.template make<void>(empty_caller());
}
template <typename... Args, typename T> auto make(T&& callback) {
return left_.template make<Args...>(std::forward<T>(callback)) ||
right_.template make<void>(empty_caller());
}
};
*/
template <typename... Args> struct type_chainer {
template <typename First> auto add() {
return type_chainer<Args..., First>{};
}
template <template <typename> class T, typename First> auto add() {
return type_chainer<Args..., T<First>>{};
}
template <template <typename, typename> class T, typename First,
typename Second>
auto add() {
return type_chainer<Args..., T<First, Second>, T<First, Second>,
T<Second, First>, T<Second, Second>>{};
}
using type = testing::Types<Args...>;
};
inline auto make_type() {
type_chainer<> chainer{};
return chainer // ...
.add<provide_copyable>()
.add<provide_unique>()
.add<provide_copyable_erasure>()
.add<provide_unique_erasure>();
}
using single_types = decltype(make_type())::type;
struct tag1 {};
struct tag2 {};
struct tag3 {};
template <typename Provider>
struct single_dimension_tests : continuation_provider<Provider> {};
TYPED_TEST_CASE(single_dimension_tests, single_types);
#endif // TEST_CONTINUABLE_HPP__