| @@ -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); | |||||
| } | |||||