@@ -0,0 +1 @@ | |||
build/** |
@@ -0,0 +1,3 @@ | |||
[submodule "cmake/modules"] | |||
path = cmake/modules | |||
url = b3rgmann@git.bergmann89.de:cpp/CMakeModules.git |
@@ -0,0 +1,27 @@ | |||
{ | |||
// Use IntelliSense to learn about possible attributes. | |||
// Hover to view descriptions of existing attributes. | |||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | |||
"version": "0.2.0", | |||
"configurations": [ | |||
{ | |||
"name": "future tests", | |||
"type": "cppdbg", | |||
"request": "launch", | |||
"program": "${workspaceFolder}/build/test/test-asyncpp-future-tests", | |||
"args": [], | |||
"stopAtEntry": false, | |||
"cwd": "${workspaceFolder}", | |||
"environment": [], | |||
"externalConsole": false, | |||
"MIMode": "gdb", | |||
"setupCommands": [ | |||
{ | |||
"description": "Enable pretty-printing for gdb", | |||
"text": "-enable-pretty-printing", | |||
"ignoreFailures": true | |||
} | |||
] | |||
} | |||
] | |||
} |
@@ -0,0 +1,53 @@ | |||
{ | |||
"files.associations": { | |||
"optional": "cpp", | |||
"memory": "cpp", | |||
"type_traits": "cpp", | |||
"variant": "cpp", | |||
"array": "cpp", | |||
"atomic": "cpp", | |||
"*.tcc": "cpp", | |||
"cctype": "cpp", | |||
"clocale": "cpp", | |||
"cmath": "cpp", | |||
"cstdarg": "cpp", | |||
"cstdint": "cpp", | |||
"cstdio": "cpp", | |||
"cstdlib": "cpp", | |||
"ctime": "cpp", | |||
"cwchar": "cpp", | |||
"cwctype": "cpp", | |||
"deque": "cpp", | |||
"unordered_map": "cpp", | |||
"vector": "cpp", | |||
"exception": "cpp", | |||
"fstream": "cpp", | |||
"functional": "cpp", | |||
"initializer_list": "cpp", | |||
"iomanip": "cpp", | |||
"iosfwd": "cpp", | |||
"iostream": "cpp", | |||
"istream": "cpp", | |||
"limits": "cpp", | |||
"new": "cpp", | |||
"ostream": "cpp", | |||
"numeric": "cpp", | |||
"sstream": "cpp", | |||
"stdexcept": "cpp", | |||
"streambuf": "cpp", | |||
"system_error": "cpp", | |||
"cinttypes": "cpp", | |||
"tuple": "cpp", | |||
"utility": "cpp", | |||
"typeinfo": "cpp", | |||
"cstddef": "cpp", | |||
"string_view": "cpp", | |||
"algorithm": "cpp", | |||
"iterator": "cpp", | |||
"map": "cpp", | |||
"memory_resource": "cpp", | |||
"random": "cpp", | |||
"set": "cpp", | |||
"string": "cpp" | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
# Initialize CMake ################################################################################ | |||
CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR ) | |||
# Set CMAKE_BUILD_TYPE | |||
If ( NOT CMAKE_BUILD_TYPE ) | |||
Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) | |||
EndIf ( NOT CMAKE_BUILD_TYPE ) | |||
Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel ) | |||
# Set CMAKE_MODULE_PATH | |||
If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) | |||
Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | |||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) | |||
EndIf ( ) | |||
If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/cmake" ) | |||
Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | |||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/cmake" ) | |||
EndIf ( ) | |||
# Project ######################################################################################### | |||
Include ( GNUInstallDirs ) | |||
Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/asyncpp-options.cmake ) | |||
Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/asyncpp-const.cmake ) | |||
Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/asyncpp-var.cmake ) | |||
Project ( ${ASYNCPP_PROJECT_NAME} | |||
DESCRIPTION "${ASYNCPP_PROJECT_DESCRIPTION}" | |||
VERSION "${ASYNCPP_VERSION}" ) | |||
Include ( CTest ) | |||
# Subdirectories | |||
Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) | |||
Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) | |||
# Install | |||
If ( NOT ASYNCPP_HAS_EXPORT | |||
OR NOT ASYNCPP_INSTALL_PACKAGE ) | |||
Return ( ) | |||
EndIf ( ) | |||
Include ( CMakePackageConfigHelpers ) | |||
Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/asyncpp-config-version.cmake" | |||
VERSION ${ASYNCPP_VERSION} | |||
COMPATIBILITY AnyNewerVersion ) | |||
Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/asyncpp-config.cmake" | |||
"${CMAKE_CURRENT_BINARY_DIR}/cmake/asyncpp-config.cmake" | |||
@ONLY ) | |||
Set ( ConfigPackageLocation "${ASYNCPP_INSTALL_DIR_SHARE}/cmake" ) | |||
Install ( EXPORT | |||
asyncpp | |||
NAMESPACE | |||
asyncpp:: | |||
DESTINATION | |||
${ConfigPackageLocation} ) | |||
Install ( FILES | |||
"${CMAKE_CURRENT_BINARY_DIR}/cmake/asyncpp-config.cmake" | |||
"${CMAKE_CURRENT_BINARY_DIR}/cmake/asyncpp-config-version.cmake" | |||
DESTINATION | |||
${ConfigPackageLocation} | |||
COMPONENT | |||
Devel ) |
@@ -0,0 +1,5 @@ | |||
# asyncpp | |||
This is a C++ library that implements handling of asynchronous tasks. | |||
It is based on the idea of the [rust tokio crate](https://github.com/tokio-rs/tokio). |
@@ -0,0 +1,9 @@ | |||
# asyncpp-config.cmake - package configuration file | |||
# Include ( CMakeFindDependencyMacro ) | |||
# Find_Dependency ( <dependency> ) | |||
Include ( FindPackageHandleStandardArgs ) | |||
Set ( ${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE} ) | |||
Find_Package_Handle_Standard_Args ( asyncpp CONFIG_MODE ) | |||
Include ( "${CMAKE_CURRENT_LIST_DIR}/asyncpp.cmake") |
@@ -0,0 +1,28 @@ | |||
# This file contains constant variables that are fixed to this project | |||
# Version | |||
Set ( ASYNCPP_VERSION_MAJOR 1 ) | |||
Set ( ASYNCPP_VERSION_MINOR 0 ) | |||
Set ( ASYNCPP_VERSION_PATCH 0 ) | |||
Set ( ASYNCPP_VERSION_BUILD 0 ) | |||
Set ( ASYNCPP_VERSION_HASH "" ) | |||
Set ( ASYNCPP_VERSION_BEHIND 0 ) | |||
Set ( ASYNCPP_VERSION_DIRTY 0 ) | |||
# Names | |||
Set ( ASYNCPP_PROJECT_NAME "asyncpp" ) | |||
Set ( ASYNCPP_PROJECT_DESCRIPTION "A simple interface library" ) | |||
# Include generated variables for further usage | |||
Include ( ${CMAKE_CURRENT_LIST_DIR}/asyncpp-var.cmake ) | |||
# Install directories | |||
Set ( ASYNCPP_INSTALL_DIR_INCLUDE "${CMAKE_INSTALL_INCLUDEDIR}/${ASYNCPP_NAME}" ) | |||
Set ( ASYNCPP_INSTALL_DIR_LIB "${CMAKE_INSTALL_LIBDIR}" ) | |||
Set ( ASYNCPP_INSTALL_DIR_SHARE "${CMAKE_INSTALL_DATAROOTDIR}/${ASYNCPP_NAME}" ) | |||
# C Standard | |||
Set ( CMAKE_C_STANDARD 11 ) | |||
Set ( CMAKE_CXX_STANDARD 17 ) | |||
Set ( CMAKE_C_STANDARD_REQUIRED ON ) | |||
Set ( CMAKE_CXX_STANDARD_REQUIRED ON ) |
@@ -0,0 +1,11 @@ | |||
# This file contains options that can be passed to the cmake command | |||
Option ( ASYNCPP_INSTALL_HEADER | |||
"Install headers of asyncpp." | |||
ON ) | |||
Option ( ASYNCPP_INSTALL_PACKAGE | |||
"Install the cmake package of asyncpp." | |||
ON ) | |||
Option ( ASYNCPP_USE_GIT_VERSION | |||
"Read the git tags to get the version of asyncpp" | |||
ON ) |
@@ -0,0 +1,32 @@ | |||
# This file contains generated variables that are needed for the project | |||
# Git Version | |||
If ( ASYNCPP_USE_GIT_VERSION ) | |||
Include ( git_helper OPTIONAL RESULT_VARIABLE HAS_GIT_HELPER ) | |||
If ( HAS_GIT_HELPER ) | |||
GitGetVersion ( ${CMAKE_CURRENT_LIST_DIR}/.. | |||
ASYNCPP_VERSION_MAJOR | |||
ASYNCPP_VERSION_MINOR | |||
ASYNCPP_VERSION_PATCH | |||
ASYNCPP_VERSION_BUILD | |||
ASYNCPP_VERSION_HASH | |||
ASYNCPP_VERSION_BEHIND | |||
ASYNCPP_VERSION_DIRTY ) | |||
EndIf ( ) | |||
EndIf ( ) | |||
# Strings | |||
Set ( ASYNCPP_VERSION_SHORT | |||
"${ASYNCPP_VERSION_MAJOR}.${ASYNCPP_VERSION_MINOR}" ) | |||
Set ( ASYNCPP_VERSION | |||
"${ASYNCPP_VERSION_SHORT}.${ASYNCPP_VERSION_PATCH}.${ASYNCPP_VERSION_BUILD}" ) | |||
Set ( ASYNCPP_VERSION_COMPLETE | |||
"${ASYNCPP_VERSION}" ) | |||
Set ( ASYNCPP_NAME | |||
"${ASYNCPP_PROJECT_NAME}-${ASYNCPP_VERSION_SHORT}" ) | |||
Set ( ASYNCPP_OUTPUTNAME | |||
"${ASYNCPP_PROJECT_NAME}" ) | |||
If ( ASYNCPP_VERSION_BEHIND ) | |||
Set ( ASYNCPP_VERSION_COMPLETE | |||
"${ASYNCPP_VERSION_COMPLETE}+${ASYNCPP_VERSION_BEHIND}" ) | |||
EndIf ( ) |
@@ -0,0 +1 @@ | |||
Subproject commit 94b9877d65e46c9d8169ebc46f163d02e4d9dcf3 |
@@ -0,0 +1,7 @@ | |||
#pragma once | |||
#include <asyncpp/future.h> | |||
#include <asyncpp/result.h> | |||
#include <asyncpp/future.inl> | |||
#include <asyncpp/result.inl> |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <type_traits> | |||
#include "result.h" | |||
#include "future.pre.h" | |||
#include "future/map.h" | |||
#include "future/and_then.h" | |||
namespace asyncpp | |||
{ | |||
template< | |||
typename T_object, | |||
typename T_impl> | |||
struct future | |||
{ | |||
using object_type = T_object; | |||
using clean_object_type = std::decay_t<object_type>; | |||
using impl_type = impl::future<clean_object_type>; | |||
using value_type = typename impl_type::value_type; | |||
using result_type = result<value_type>; | |||
object_type ref; | |||
/** | |||
* @brief Value constructor. | |||
*/ | |||
template<typename X_object> | |||
inline future(X_object&& p_ref); | |||
/** | |||
* @brief Function that will be called repeatedly to check if the future is ready. | |||
*/ | |||
inline result_type poll(); | |||
/** | |||
* @brief Transform the result of this future. | |||
*/ | |||
template<typename X_lambda> | |||
inline auto map(X_lambda&& p_lambda); | |||
/** | |||
* @brief Execute the given lambda after the future is finished and | |||
* wait for the future returned by the lambda. | |||
*/ | |||
template<typename X_lambda> | |||
inline auto and_then(X_lambda&& p_lambda); | |||
}; | |||
} |
@@ -0,0 +1,93 @@ | |||
#pragma once | |||
#include "future.h" | |||
#include "future/map.inl" | |||
#include "future/and_then.inl" | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
template<typename T_impl> | |||
template<typename X_future, typename X_lambda> | |||
auto future_base<T_impl> | |||
::map(X_future&& self, X_lambda&& p_lambda) | |||
{ | |||
using future_type = X_future; | |||
using lambda_type = X_lambda; | |||
using map_type = future_impl::map_impl<future_type, lambda_type>; | |||
return as_future(map_type( | |||
std::forward<X_future>(self), | |||
std::forward<X_lambda>(p_lambda))); | |||
} | |||
template<typename T_impl> | |||
template<typename X_future, typename X_lambda> | |||
auto future_base<T_impl> | |||
::and_then(X_future&& self, X_lambda&& p_lambda) | |||
{ | |||
using future_type = X_future; | |||
using lambda_type = X_lambda; | |||
using and_then_type = future_impl::and_then_impl<future_type, lambda_type>; | |||
return as_future(and_then_type( | |||
std::forward<X_future>(self), | |||
std::forward<X_lambda>(p_lambda))); | |||
} | |||
} | |||
/* future */ | |||
template< | |||
typename T_value, | |||
typename T_impl> | |||
template< | |||
typename X_object> | |||
future<T_value, T_impl> | |||
::future(X_object&& p_ref) | |||
: ref(std::forward<X_object>(p_ref)) | |||
{ } | |||
template< | |||
typename T_value, | |||
typename T_impl> | |||
typename future<T_value, T_impl>::result_type | |||
future<T_value, T_impl> | |||
::poll() | |||
{ return impl_type::poll(*this); } | |||
template< | |||
typename T_value, | |||
typename T_impl> | |||
template< | |||
typename X_lambda> | |||
auto future<T_value, T_impl> | |||
::map(X_lambda&& p_lambda) | |||
{ return impl_type::map(std::move(*this), std::forward<X_lambda>(p_lambda)); } | |||
template< | |||
typename T_value, | |||
typename T_impl> | |||
template< | |||
typename X_lambda> | |||
auto future<T_value, T_impl> | |||
::and_then(X_lambda&& p_lambda) | |||
{ return impl_type::and_then(std::move(*this), std::forward<X_lambda>(p_lambda)); } | |||
/* misc */ | |||
template<typename X_value> | |||
constexpr decltype(auto) as_future(X_value&& value) | |||
{ | |||
using value_type = X_value; | |||
using future_type = future<value_type>; | |||
return future_type(std::forward<X_value>(value)); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#pragma once | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
template<typename T_impl> | |||
struct future_base | |||
{ | |||
template<typename T_future> | |||
static inline auto poll(T_future& self) = delete; | |||
template<typename T_future, typename T_lambda> | |||
static inline auto map(T_future&& self, T_lambda&& p_lambda); | |||
template<typename T_future, typename T_lambda> | |||
static inline auto and_then(T_future&& self, T_lambda&& p_lambda); | |||
}; | |||
template<typename T, typename = void> | |||
struct future; | |||
} | |||
template< | |||
typename T_object, | |||
typename T_impl = impl::future<std::decay_t<T_object>>> | |||
struct future; | |||
/** | |||
* @brief Construct a future from the given value. | |||
*/ | |||
template<typename X_value> | |||
constexpr decltype(auto) as_future(X_value&& value); | |||
} |
@@ -0,0 +1,37 @@ | |||
#pragma once | |||
#include <memory> | |||
namespace asyncpp { | |||
namespace future_impl { | |||
template< | |||
typename T_future, | |||
typename T_lambda> | |||
struct and_then_impl | |||
{ | |||
public: | |||
using lambda_type = T_lambda; | |||
using outer_future_type = T_future; | |||
using outer_value_type = typename outer_future_type::value_type; | |||
using inner_future_type = decltype(as_future(std::declval<lambda_type>()(std::declval<outer_value_type>()))); | |||
using inner_future_type_ptr = std::unique_ptr<inner_future_type>; | |||
public: | |||
lambda_type lambda; | |||
outer_future_type outer; | |||
inner_future_type_ptr inner; | |||
public: | |||
/** | |||
* @brief Constructor. | |||
*/ | |||
template< | |||
typename X_future, | |||
typename X_lambda> | |||
inline and_then_impl( | |||
X_future&& p_outer, | |||
X_lambda&& p_lambda); | |||
}; | |||
} } |
@@ -0,0 +1,64 @@ | |||
#pragma once | |||
#include "map.h" | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
template< | |||
typename T_future, | |||
typename T_lambda> | |||
struct future<future_impl::and_then_impl<T_future, T_lambda>, void> | |||
: public future_base<future<future_impl::and_then_impl<T_future, T_lambda>, void>> | |||
{ | |||
using and_then_type = future_impl::and_then_impl<T_future, T_lambda>; | |||
using inner_future_type = typename and_then_type::inner_future_type; | |||
using value_type = typename inner_future_type::value_type; | |||
using result_type = typename inner_future_type::result_type; | |||
template<typename X_future> | |||
static inline auto poll(X_future& self) | |||
{ | |||
while (true) | |||
{ | |||
if (self.ref.inner) | |||
{ | |||
return self.ref.inner->poll(); | |||
} | |||
else | |||
{ | |||
auto r = self.ref.outer.poll(); | |||
if (!r) | |||
return result_type::not_ready(); | |||
self.ref.inner = std::make_unique<inner_future_type>(as_future(self.ref.lambda(*r))); | |||
} | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
namespace asyncpp { | |||
namespace future_impl { | |||
template< | |||
typename T_future, | |||
typename T_lambda> | |||
template< | |||
typename X_future, | |||
typename X_lambda> | |||
and_then_impl<T_future, T_lambda> | |||
::and_then_impl( | |||
X_future&& p_outer, | |||
X_lambda&& p_lambda) | |||
: outer (std::forward<X_future>(p_outer)) | |||
, lambda(std::forward<X_lambda>(p_lambda)) | |||
{ } | |||
} } |
@@ -0,0 +1,31 @@ | |||
#pragma once | |||
namespace asyncpp { | |||
namespace future_impl { | |||
template< | |||
typename T_future, | |||
typename T_lambda> | |||
struct map_impl | |||
{ | |||
public: | |||
using future_type = T_future; | |||
using lambda_type = T_lambda; | |||
public: | |||
future_type future; | |||
lambda_type lambda; | |||
public: | |||
/** | |||
* @brief Constructor. | |||
*/ | |||
template< | |||
typename X_future, | |||
typename X_lambda> | |||
inline map_impl( | |||
X_future&& p_future, | |||
X_lambda&& p_lambda); | |||
}; | |||
} } |
@@ -0,0 +1,55 @@ | |||
#pragma once | |||
#include "map.h" | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
template< | |||
typename T_future, | |||
typename T_lambda> | |||
struct future<future_impl::map_impl<T_future, T_lambda>, void> | |||
: public future_base<future<future_impl::map_impl<T_future, T_lambda>, void>> | |||
{ | |||
using future_type = T_future; | |||
using inner_value_type = typename future_type::value_type; | |||
using lambda_type = T_lambda; | |||
using value_type = decltype(std::declval<lambda_type>()(std::declval<inner_value_type>())); | |||
template<typename X_future> | |||
static inline auto poll(X_future& self) | |||
{ | |||
using result_type = result<value_type>; | |||
auto r = self.ref.future.poll(); | |||
return r | |||
? result_type::ready(self.ref.lambda(*r)) | |||
: result_type::not_ready(); | |||
} | |||
}; | |||
} | |||
} | |||
namespace asyncpp { | |||
namespace future_impl { | |||
template< | |||
typename T_future, | |||
typename T_lambda> | |||
template< | |||
typename X_future, | |||
typename X_lambda> | |||
map_impl<T_future, T_lambda> | |||
::map_impl( | |||
X_future&& p_future, | |||
X_lambda&& p_lambda) | |||
: future(std::forward<X_future>(p_future)) | |||
, lambda(std::forward<X_lambda>(p_lambda)) | |||
{ } | |||
} } |
@@ -0,0 +1,118 @@ | |||
#pragma once | |||
#include <variant> | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
struct result_not_ready | |||
{ }; | |||
struct result_done | |||
{ }; | |||
template<typename T_value> | |||
struct result_ready | |||
{ | |||
using value_type = T_value; | |||
value_type value; | |||
template<typename... T_args> | |||
inline result_ready(T_args&&... p_args); | |||
}; | |||
} | |||
enum class result_status | |||
{ | |||
unknown = 0, | |||
not_ready, | |||
ready, | |||
done, | |||
}; | |||
template<typename T_value> | |||
struct result | |||
{ | |||
public: | |||
using value_type = T_value; | |||
using not_ready_type = impl::result_not_ready; | |||
using ready_type = impl::result_ready<value_type>; | |||
using done_type = impl::result_done; | |||
using storage_type = std::variant<not_ready_type, ready_type, done_type>; | |||
using clean_value_type = std::remove_reference_t<value_type>; | |||
using pointer_type = clean_value_type*; | |||
using reference_type = clean_value_type&; | |||
using const_pointer_type = clean_value_type const *; | |||
using const_reference_type = clean_value_type const &; | |||
private: | |||
storage_type _storage; //!< Stores the actual result. | |||
private: | |||
/** | |||
* @brief Constructor. | |||
*/ | |||
inline result(storage_type&& p_storage); | |||
public: | |||
/** | |||
* @brief returns a result that is not ready. | |||
*/ | |||
static inline auto& not_ready(); | |||
/** | |||
* @brief returns a result that is not ready. | |||
*/ | |||
template<typename... X_args> | |||
static inline auto ready(X_args&&... p_args); | |||
/** | |||
* @brief returns a result that is not ready. | |||
*/ | |||
static inline auto& done(); | |||
public: | |||
/** | |||
* @brief Get the status of the result. | |||
*/ | |||
inline result_status status() const; | |||
/** | |||
* @brief Check if the result is not ready (is pending). | |||
*/ | |||
inline bool is_not_ready() const; | |||
/** | |||
* @brief Check if the result is ready (has a value). | |||
*/ | |||
inline bool is_ready() const; | |||
/** | |||
* @brief Check if the result is done (stream is finished). | |||
*/ | |||
inline bool is_done() const; | |||
/** | |||
* @brief Get the value of the result. | |||
*/ | |||
inline reference_type value(); | |||
/** | |||
* @brief Get the value of the result. | |||
*/ | |||
inline const_reference_type value() const; | |||
public: | |||
inline operator bool() const; | |||
inline pointer_type operator-> (); | |||
inline reference_type operator* (); | |||
inline const_pointer_type operator-> () const; | |||
inline const_reference_type operator* () const; | |||
}; | |||
} |
@@ -0,0 +1,179 @@ | |||
#pragma once | |||
#include "result.h" | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
/* result_ready */ | |||
template<typename T_value> | |||
template<typename... T_args> | |||
result_ready<T_value> | |||
::result_ready(T_args&&... p_args) | |||
: value(std::forward<T_args>(p_args)...) | |||
{ } | |||
} | |||
/* result */ | |||
template<typename T_value> | |||
result<T_value> | |||
::result(storage_type&& p_storage) | |||
: _storage(std::move(p_storage)) | |||
{ } | |||
template<typename T_value> | |||
auto& result<T_value> | |||
::not_ready() | |||
{ | |||
static const result ret(storage_type(not_ready_type { })); | |||
return ret; | |||
} | |||
template<typename T_value> | |||
template<typename... X_args> | |||
auto result<T_value> | |||
::ready(X_args&&... p_args) | |||
{ return result(storage_type(ready_type(std::forward<X_args>(p_args)...))); } | |||
template<typename T_value> | |||
auto& result<T_value> | |||
::done() | |||
{ | |||
static const result ret(storage_type(done_type { })); | |||
return ret; | |||
} | |||
template<typename T_value> | |||
result_status result<T_value> | |||
::status() const | |||
{ | |||
if (is_not_ready()) | |||
return result_status::not_ready; | |||
else if (is_ready()) | |||
return result_status::ready; | |||
else if (is_done()) | |||
return result_status::done; | |||
else | |||
return result_status::unknown; | |||
} | |||
template<typename T_value> | |||
bool result<T_value> | |||
::is_not_ready() const | |||
{ return std::holds_alternative<not_ready_type>(_storage); } | |||
template<typename T_value> | |||
bool result<T_value> | |||
::is_ready() const | |||
{ return std::holds_alternative<ready_type>(_storage); } | |||
template<typename T_value> | |||
bool result<T_value> | |||
::is_done() const | |||
{ return std::holds_alternative<done_type>(_storage); } | |||
template<typename T_value> | |||
typename result<T_value>::reference_type | |||
result<T_value> | |||
::value() | |||
{ return std::get<ready_type>(_storage).value; } | |||
template<typename T_value> | |||
typename result<T_value>::const_reference_type | |||
result<T_value> | |||
::value() const | |||
{ return std::get<ready_type>(_storage).value; } | |||
template<typename T_value> | |||
result<T_value> | |||
::operator bool() const | |||
{ return is_ready(); } | |||
template<typename T_value> | |||
typename result<T_value>::pointer_type | |||
result<T_value> | |||
::operator-> () | |||
{ return &value(); } | |||
template<typename T_value> | |||
typename result<T_value>::reference_type | |||
result<T_value> | |||
::operator* () | |||
{ return value(); } | |||
template<typename T_value> | |||
typename result<T_value>::const_pointer_type | |||
result<T_value> | |||
::operator-> () const | |||
{ return &value(); } | |||
template<typename T_value> | |||
typename result<T_value>::const_reference_type | |||
result<T_value> | |||
::operator* () const | |||
{ return value(); } | |||
template<> | |||
struct result<void> | |||
{ | |||
public: | |||
using value_type = void; | |||
private: | |||
result_status _status; | |||
private: | |||
inline result(result_status p_status) | |||
: _status(p_status) | |||
{ } | |||
public: | |||
static inline auto& not_ready() | |||
{ | |||
static const result ret { result_status::not_ready }; | |||
return ret; | |||
} | |||
template<typename... X_args> | |||
static inline auto& ready(X_args&&... p_args) | |||
{ | |||
static const result ret { result_status::ready }; | |||
return ret; | |||
} | |||
static inline auto& done() | |||
{ | |||
static const result ret { result_status::done }; | |||
return ret; | |||
} | |||
public: | |||
inline result_status status() const | |||
{ return _status; } | |||
inline bool is_not_ready() const | |||
{ return _status == result_status::not_ready; } | |||
inline bool is_ready() const | |||
{ return _status == result_status::ready; } | |||
inline bool is_done() const | |||
{ return _status == result_status::done; } | |||
inline void value() | |||
{ throw std::runtime_error("'void' result does not store any value!"); } | |||
inline operator bool() const | |||
{ return _status == result_status::ready; } | |||
inline void operator* () | |||
{ value(); } | |||
}; | |||
} |
@@ -0,0 +1,30 @@ | |||
# Initialize ###################################################################################### | |||
Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) | |||
Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||
Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | |||
# Interface Library ############################################################################### | |||
Set ( ASYNCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) | |||
Add_Library ( asyncpp INTERFACE ) | |||
Target_Include_Directories ( asyncpp | |||
INTERFACE | |||
$<BUILD_INTERFACE:${ASYNCPP_INCLUDE_DIR}> | |||
$<INSTALL_INTERFACE:${ASYNCPP_INSTALL_DIR_INCLUDE}> ) | |||
# Install ######################################################################################### | |||
Set ( ASYNCPP_HAS_EXPORT False PARENT_SCOPE ) | |||
# Header | |||
If ( ASYNCPP_INSTALL_HEADER ) | |||
Set ( ASYNCPP_HAS_EXPORT True PARENT_SCOPE ) | |||
Install ( FILES ${ASYNCPP_INCLUDE_DIR}/asyncpp.h | |||
DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) | |||
Install ( DIRECTORY ${ASYNCPP_INCLUDE_DIR}/asyncpp | |||
DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) | |||
Install ( TARGETS asyncpp | |||
EXPORT asyncpp | |||
DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) | |||
EndIf ( ) |
@@ -0,0 +1,59 @@ | |||
# Initialize ###################################################################################### | |||
Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) | |||
Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||
Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) | |||
Find_Package ( Sanitizers QUIET ) | |||
# Test ############################################################################################ | |||
Find_Package ( GTest ) | |||
If ( NOT "${GTest_FOUND}" ) | |||
Return ( ) | |||
EndIf ( ) | |||
File ( GLOB_RECURSE ASYNCPP_TEST_HEADER_FILES | |||
${CMAKE_CURRENT_SOURCE_DIR}/*.h ) | |||
File ( GLOB_RECURSE ASYNCPP_TEST_INLINE_FILES | |||
${CMAKE_CURRENT_SOURCE_DIR}/*.inl ) | |||
File ( GLOB_RECURSE ASYNCPP_TEST_SOURCE_FILES | |||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} | |||
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
ForEach ( FILE IN LISTS ASYNCPP_TEST_SOURCE_FILES ) | |||
# add test | |||
Get_Filename_Component ( TEST_DIR ${FILE} DIRECTORY ) | |||
Get_Filename_Component ( TEST_NAME ${FILE} NAME_WE ) | |||
Set ( TEST_NAME "${TEST_DIR}/${TEST_NAME}" ) | |||
String ( REPLACE "\\" "-" TEST_NAME "${TEST_NAME}" ) | |||
String ( REPLACE "/" "-" TEST_NAME "${TEST_NAME}" ) | |||
String ( REPLACE "_" "-" TEST_NAME "${TEST_NAME}" ) | |||
Set ( TEST_NAME "test-${TEST_NAME}" ) | |||
Add_Executable ( ${TEST_NAME} | |||
EXCLUDE_FROM_ALL | |||
${ASYNCPP_TEST_HEADER_FILES} | |||
${ASYNCPP_TEST_INLINE_FILES} | |||
${FILE} ) | |||
Target_Link_Libraries ( ${TEST_NAME} | |||
PUBLIC | |||
asyncpp | |||
GTest::Main ) | |||
# Sanitizers | |||
If ( Sanitizers_FOUND ) | |||
Add_Sanitizers ( ${TEST_NAME} ) | |||
EndIf ( ) | |||
# pedantic | |||
If ( HAS_PEDANTIC ) | |||
Pedantic_Apply_Flags_Target ( ${TEST_NAME} ALL ) | |||
EndIf ( ) | |||
# test | |||
If ( HAS_CMAKE_TESTS ) | |||
Add_CMake_Test ( NAME ${TEST_NAME} TARGET ${TEST_NAME} GROUP asyncpp ) | |||
Else ( ) | |||
Add_Test ( NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) | |||
EndIf ( ) | |||
EndForEach ( ) |
@@ -0,0 +1,111 @@ | |||
#include <gtest/gtest.h> | |||
#include <asyncpp.h> | |||
using namespace ::testing; | |||
using namespace ::asyncpp; | |||
struct delay | |||
{ | |||
int const delay { 5 }; | |||
int count { 0 }; | |||
}; | |||
namespace asyncpp | |||
{ | |||
namespace impl | |||
{ | |||
template<> | |||
struct future<delay, void> | |||
: public future_base<future<delay, void>> | |||
{ | |||
using value_type = int&; | |||
template<typename T_future> | |||
static inline auto poll(T_future& self) | |||
{ | |||
using result_type = result<value_type>; | |||
if (self.ref.count >= self.ref.delay) | |||
return result_type::ready(self.ref.count); | |||
++self.ref.count; | |||
return result_type::not_ready(); | |||
} | |||
}; | |||
} | |||
} | |||
TEST(future_tests, poll) | |||
{ | |||
delay d { 5, 0 }; | |||
auto f = as_future(d); | |||
auto r0 = f.poll(); | |||
ASSERT_EQ(result_status::not_ready, r0.status()); | |||
auto r1 = f.poll(); | |||
ASSERT_EQ(result_status::not_ready, r1.status()); | |||
auto r2 = f.poll(); | |||
ASSERT_EQ(result_status::not_ready, r2.status()); | |||
auto r3 = f.poll(); | |||
ASSERT_EQ(result_status::not_ready, r3.status()); | |||
auto r4 = f.poll(); | |||
ASSERT_EQ(result_status::not_ready, r4.status()); | |||
auto r = f.poll(); | |||
ASSERT_TRUE(r); | |||
ASSERT_EQ (result_status::ready, r.status()); | |||
ASSERT_EQ (5, *r); | |||
ASSERT_EQ (5, d.count); | |||
ASSERT_EQ (&*r, &d.count); | |||
} | |||
TEST(future_tests, map) | |||
{ | |||
delay d { 1, 1 }; | |||
auto f = as_future(d) | |||
.map([](int& i){ | |||
return 10 + i; | |||
}); | |||
auto r = f.poll(); | |||
ASSERT_TRUE(r); | |||
ASSERT_EQ (11, *r); | |||
} | |||
TEST(future_tests, and_then) | |||
{ | |||
delay d { 2, 0 }; | |||
auto f = as_future(d) | |||
.and_then([](int& i){ | |||
return delay { 5, i }; | |||
}); | |||
auto r0 = f.poll(); | |||
ASSERT_FALSE(r0); | |||
auto r1 = f.poll(); | |||
ASSERT_FALSE(r1); | |||
auto r2 = f.poll(); | |||
ASSERT_FALSE(r2); | |||
auto r3 = f.poll(); | |||
ASSERT_FALSE(r3); | |||
auto r4 = f.poll(); | |||
ASSERT_FALSE(r4); | |||
auto r = f.poll(); | |||
ASSERT_TRUE(r); | |||
ASSERT_EQ (5, *r); | |||
ASSERT_EQ (2, d.count); | |||
} |
@@ -0,0 +1,49 @@ | |||
#include <gtest/gtest.h> | |||
#include <asyncpp.h> | |||
using namespace ::testing; | |||
using namespace ::asyncpp; | |||
TEST(result_tests, not_ready) | |||
{ | |||
using result_type = result<int>; | |||
auto r = result_type::not_ready(); | |||
EXPECT_EQ (result_status::not_ready, r.status()); | |||
EXPECT_TRUE (r.is_not_ready()); | |||
EXPECT_FALSE (r.is_ready()); | |||
EXPECT_FALSE (r.is_done()); | |||
EXPECT_FALSE (r); | |||
EXPECT_ANY_THROW(*r); | |||
} | |||
TEST(result_tests, ready) | |||
{ | |||
using result_type = result<int&>; | |||
int i; | |||
auto r = result_type::ready(i); | |||
EXPECT_EQ (result_status::ready, r.status()); | |||
EXPECT_FALSE (r.is_not_ready()); | |||
EXPECT_TRUE (r.is_ready()); | |||
EXPECT_FALSE (r.is_done()); | |||
EXPECT_TRUE (r); | |||
EXPECT_EQ (&*r, &i); | |||
} | |||
TEST(result_tests, done) | |||
{ | |||
using result_type = result<void>; | |||
auto r = result_type::done(); | |||
EXPECT_EQ (result_status::done, r.status()); | |||
EXPECT_FALSE (r.is_not_ready()); | |||
EXPECT_FALSE (r.is_ready()); | |||
EXPECT_TRUE (r.is_done()); | |||
EXPECT_FALSE (r); | |||
EXPECT_ANY_THROW(*r); | |||
} |