Browse Source

* Initial commit (implemented future and result)

master
bergmann 4 years ago
commit
3d76078c5c
25 changed files with 1155 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +3
    -0
      .gitmodules
  3. +27
    -0
      .vscode/launch.json
  4. +53
    -0
      .vscode/settings.json
  5. +63
    -0
      CMakeLists.txt
  6. +5
    -0
      README.md
  7. +9
    -0
      cmake/asyncpp-config.cmake
  8. +28
    -0
      cmake/asyncpp-const.cmake
  9. +11
    -0
      cmake/asyncpp-options.cmake
  10. +32
    -0
      cmake/asyncpp-var.cmake
  11. +1
    -0
      cmake/modules
  12. +7
    -0
      include/asyncpp.h
  13. +51
    -0
      include/asyncpp/future.h
  14. +93
    -0
      include/asyncpp/future.inl
  15. +38
    -0
      include/asyncpp/future.pre.h
  16. +37
    -0
      include/asyncpp/future/and_then.h
  17. +64
    -0
      include/asyncpp/future/and_then.inl
  18. +31
    -0
      include/asyncpp/future/map.h
  19. +55
    -0
      include/asyncpp/future/map.inl
  20. +118
    -0
      include/asyncpp/result.h
  21. +179
    -0
      include/asyncpp/result.inl
  22. +30
    -0
      src/CMakeLists.txt
  23. +59
    -0
      test/CMakeLists.txt
  24. +111
    -0
      test/asyncpp/future_tests.cpp
  25. +49
    -0
      test/asyncpp/result_tests.cpp

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
build/**

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
[submodule "cmake/modules"]
path = cmake/modules
url = b3rgmann@git.bergmann89.de:cpp/CMakeModules.git

+ 27
- 0
.vscode/launch.json View File

@@ -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
}
]
}
]
}

+ 53
- 0
.vscode/settings.json View File

@@ -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"
}
}

+ 63
- 0
CMakeLists.txt View File

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

+ 5
- 0
README.md View File

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

+ 9
- 0
cmake/asyncpp-config.cmake View File

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

+ 28
- 0
cmake/asyncpp-const.cmake View File

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

+ 11
- 0
cmake/asyncpp-options.cmake View File

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

+ 32
- 0
cmake/asyncpp-var.cmake View File

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

+ 1
- 0
cmake/modules

@@ -0,0 +1 @@
Subproject commit 94b9877d65e46c9d8169ebc46f163d02e4d9dcf3

+ 7
- 0
include/asyncpp.h View File

@@ -0,0 +1,7 @@
#pragma once

#include <asyncpp/future.h>
#include <asyncpp/result.h>

#include <asyncpp/future.inl>
#include <asyncpp/result.inl>

+ 51
- 0
include/asyncpp/future.h View File

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

}

+ 93
- 0
include/asyncpp/future.inl View File

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

}

+ 38
- 0
include/asyncpp/future.pre.h View File

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

}

+ 37
- 0
include/asyncpp/future/and_then.h View File

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

} }

+ 64
- 0
include/asyncpp/future/and_then.inl View File

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

} }

+ 31
- 0
include/asyncpp/future/map.h View File

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

} }

+ 55
- 0
include/asyncpp/future/map.inl View File

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

} }

+ 118
- 0
include/asyncpp/result.h View File

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

}

+ 179
- 0
include/asyncpp/result.inl View File

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

}

+ 30
- 0
src/CMakeLists.txt View File

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

+ 59
- 0
test/CMakeLists.txt View File

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

+ 111
- 0
test/asyncpp/future_tests.cpp View File

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

+ 49
- 0
test/asyncpp/result_tests.cpp View File

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

Loading…
Cancel
Save