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