template<typename Data, typename Annotation>
cti::continuable_base class

The main class of the continuable library, it provides the functionality for chaining callbacks and continuations together to a unified hierarchy.

Template parameters
Data The internal data which is used to store the current continuation and intermediate lazy connection result.
Annotation The internal data used to store the current signature hint or strategy used for combining lazy connections.

The most important method is the cti::continuable_base::then() method, which allows to attach a callback to the continuable.

Use the continuable types defined in continuable/continuable.hpp, in order to use this class.

Constructors, destructors, conversion operators

continuable_base(Data data) explicit
Constructor accepting the data object while erasing the annotation.
template<typename OtherData, std::enable_if_t<detail::base::can_accept_continuation<Data, Annotation, detail::traits::unrefcv_t<OtherData>>::value>* = nullptr>
continuable_base(OtherData&& data)
Constructor accepting any object convertible to the data object, while erasing the annotation.
template<typename OData, std::enable_if_t<std::is_convertible<detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
continuable_base(continuable_base<OData, Annotation>&& other)
Constructor taking the data of other continuable_base objects while erasing the hint.
template<typename OData, typename OAnnotation>
continuable_base(continuable_base<OData, OAnnotation>&& other)
Constructor taking the data of other continuable_base objects while erasing the hint.
~continuable_base()
The destructor automatically invokes the continuable_base if it wasn't consumed yet.

Public functions

template<typename T, typename E = detail::types::this_thread_executor_tag>
auto then(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) && -> auto
Main method of the continuable_base to chain the current continuation with a new callback.
template<typename OData, typename OAnnotation>
auto then(continuable_base<OData, OAnnotation>&& continuation) && -> auto
Additional overload of the continuable_base::then() method which is accepting a continuable_base itself.
template<typename T, typename E = detail::types::this_thread_executor_tag>
auto fail(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) && -> auto
Main method of the continuable_base to catch exceptions and error codes in case the asynchronous control flow failed and was resolved through an error code or exception.
template<typename OData, typename OAnnotation>
auto fail(continuable_base<OData, OAnnotation>&& continuation) && -> auto
Additional overload of the continuable_base::fail() method which is accepting a continuable_base itself.
template<typename T, typename E = detail::types::this_thread_executor_tag>
auto next(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) && -> auto
A method which allows to use an overloaded callable for the error as well as the valid result path.
template<typename... Args>
auto as() && -> auto
Returns a continuable_base which will have its signature converted to the given Args.
template<typename T>
auto apply(T&& transform) && -> auto
A method which allows to apply a callable object to this continuable.
template<typename T>
auto operator|(T&& right) && -> auto
The pipe operator | is an alias for the continuable::then method.
template<typename OData, typename OAnnotation>
auto operator&&(continuable_base<OData, OAnnotation>&& right) && -> auto
Invokes both continuable_base objects parallel and calls the callback with the result of both continuable_base objects.
template<typename OData, typename OAnnotation>
auto operator||(continuable_base<OData, OAnnotation>&& right) && -> auto
Invokes both continuable_base objects parallel and calls the callback once with the first result available.
template<typename OData, typename OAnnotation>
auto operator>>(continuable_base<OData, OAnnotation>&& right) && -> auto
Invokes both continuable_base objects sequential and calls the callback with the result of both continuable_base objects.
void done() &&
Invokes the continuation chain manually even before the cti::continuable_base is destructed. This will release the object.
auto finish() && -> auto
Materializes the continuation expression template and finishes the current applied strategy such that the resulting continuable will always be a concrete type and Continuable::is_concrete holds.
auto is_ready() const -> bool noexcept
Returns true when the continuable can provide its result immediately, and its lazy invocation would be side-effect free.
auto unpack() && -> auto
Invalidates the continuable and returns its immediate invocation result.
auto is_frozen() const -> bool noexcept
Predicate to check whether the cti::continuable_base is frozen or not.
auto freeze(bool enabled = true) &noexcept -> continuable_base&
Prevents the automatic invocation of the continuation chain which happens on destruction of the continuable_base. You may still invoke the chain through the continuable_base::done method.
auto freeze(bool enabled = true) &&noexcept -> continuable_base&&
Prevents the automatic invocation of the continuation chain which happens on destruction of the continuable_base. You may still invoke the chain through the continuable_base::done method.
auto operator co_await() && -> auto
Implements the operator for awaiting on continuables using co_await.

Function documentation

template<typename Data, typename Annotation> template<typename OData, std::enable_if_t<std::is_convertible<detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
cti::continuable_base<Data, Annotation>::continuable_base(continuable_base<OData, Annotation>&& other)

Constructor taking the data of other continuable_base objects while erasing the hint.

This constructor makes it possible to replace the internal data object of the continuable by any object which is useful for type-erasure.

template<typename Data, typename Annotation> template<typename OData, typename OAnnotation>
cti::continuable_base<Data, Annotation>::continuable_base(continuable_base<OData, OAnnotation>&& other)

Constructor taking the data of other continuable_base objects while erasing the hint.

This constructor makes it possible to replace the internal data object of the continuable by any object which is useful for type-erasure.

template<typename Data, typename Annotation>
cti::continuable_base<Data, Annotation>::~continuable_base()

The destructor automatically invokes the continuable_base if it wasn't consumed yet.

In order to invoke the continuable early you may call the continuable_base::done() method.

The continuable_base::freeze method disables the automatic invocation on destruction without invalidating the object.

template<typename Data, typename Annotation> template<typename T, typename E = detail::types::this_thread_executor_tag>
auto cti::continuable_base<Data, Annotation>::then(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) &&

Main method of the continuable_base to chain the current continuation with a new callback.

Parameters
callback

The callback which is used to process the current asynchronous result on arrival. The callback is required to accept the current result at least partially (or nothing of the result).

(http_request("github.com") && http_request("atom.io"))
  .then([](std::string github, std::string atom) {
    // We use the whole result
  });

(http_request("github.com") && http_request("atom.io"))
  .then([](std::string github) {
    // We only use the result partially
  });

(http_request("github.com") && http_request("atom.io"))
  .then([] {
    // We discard the result
  });
executor

The optional executor which is used to dispatch the callback. The executor needs to accept callable objects callable through an operator() through its operator() itself. The executor can be move-only, but it's not required to. The default executor which is used when omitting the argument dispatches the callback on the current executing thread. Consider the example shown below:

auto executor = [](auto&& work) {
  // Dispatch the work here or forward it to an executor of
  // your choice.
  std::forward<decltype(work)>(work)();
};

http_request("github.com")
  .then([](std::string github) {
    // Do something...
   }, executor);
Returns

Returns a continuable_base with an asynchronous return type depending on the return value of the callback:

Callback returnsResulting type
voidcontinuable_base with <>
Argcontinuable_base with <Arg>
std::pair<First, Second>continuable_base with <First, Second>
std::tuple<Args...>continuable_base with <Args...>
cti::result<Args...>continuable_base with <Args...>
continuable_base<Arg...>continuable_base with <Args...>

Which means the result type of the continuable_base is equal to the plain types the callback returns (std::tuple and std::pair arguments are unwrapped). A single continuable_base as argument is resolved and the result type is equal to the resolved continuable_base. A cti::result can be used to cancel the continuation or to transition to the exception handler. The special unwrapping of types can be disabled through wrapping such objects through a call to cti::make_plain. Consider the following examples:

http_request("github.com")
  .then([](std::string github) { return; })
  .then([] { }); // <void>

http_request("github.com")
  .then([](std::string github) { return 0; })
  .then([](int a) { }); // <int>

http_request("github.com")
  .then([](std::string github) { return std::make_pair(1, 2); })
  .then([](int a, int b) { }); // <int, int>

http_request("github.com")
  .then([](std::string github) { return std::make_tuple(1, 2, 3); })
  .then([](int a, int b, int c) { }); // <int, int, int>

http_request("github.com")
  .then([](std::string github) { return http_request("atom.io"); })
  .then([](std::string atom) { }); // <std::string>

http_request("example.com")
  .then([](std::string content) -> result<std::string> {
    return rethrow(std::make_exception_ptr(std::exception{}));
  })
  .fail([] -> result<std::string> {
    return recover("Hello World!");
  })
  .then([](std::string content) -> result<std::string> {
    return cancel();
  })

template<typename Data, typename Annotation> template<typename OData, typename OAnnotation>
auto cti::continuable_base<Data, Annotation>::then(continuable_base<OData, OAnnotation>&& continuation) &&

Additional overload of the continuable_base::then() method which is accepting a continuable_base itself.

Parameters
continuation

A continuable_base reflecting the continuation which is used to continue the call hierarchy. The result of the current continuable is discarded and the given continuation is invoked as shown below.

http_request("github.com")
  .then(http_request("atom.io"))
  .then([](std::string atom) {
    // ...
  });
Returns Returns a continuable_base representing the next asynchronous result to continue within the asynchronous call hierarchy.

template<typename Data, typename Annotation> template<typename T, typename E = detail::types::this_thread_executor_tag>
auto cti::continuable_base<Data, Annotation>::fail(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) &&

Main method of the continuable_base to catch exceptions and error codes in case the asynchronous control flow failed and was resolved through an error code or exception.

Parameters
callback The callback which is used to process the current asynchronous error result on arrival. In case the continuable_base is using exceptions, the usage is as shown below:
executor The optional executor which is used to dispatch the callback. See the description in then above.
Returns Returns a continuable_base with an asynchronous return type depending on the previous result type.
http_request("github.com")
  .then([](std::string github) { })
  .fail([](std::exception_ptr ep) {
    // Check whether the exception_ptr is valid (not default constructed)
    // if bool(ep) == false this means that the operation was cancelled
    // by the user or application (promise.set_canceled() or
    // make_cancelling_continuable()).
    if (ep) {
      // Handle the error here
      try {
        std::rethrow_exception(ep);
      } catch (std::exception& e) {
        e.what(); // Handle the exception
      }
    }
  });

In case exceptions are disabled, std::error_condition is used as error result instead of std::exception_ptr.

http_request("github.com")
  .then([](std::string github) { })
  .fail([](std::error_condition error) {
    error.message(); // Handle the error here
  });

template<typename Data, typename Annotation> template<typename OData, typename OAnnotation>
auto cti::continuable_base<Data, Annotation>::fail(continuable_base<OData, OAnnotation>&& continuation) &&

Additional overload of the continuable_base::fail() method which is accepting a continuable_base itself.

Parameters
continuation

A continuable_base reflecting the continuation which is used to continue the call hierarchy on errors. The result of the current continuable is discarded and the given continuation is invoked as shown below.

http_request("github.com")
  .fail(http_request("atom.io"))
Returns Returns a continuable_base with an asynchronous return type depending on the previous result type.

template<typename Data, typename Annotation> template<typename T, typename E = detail::types::this_thread_executor_tag>
auto cti::continuable_base<Data, Annotation>::next(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) &&

A method which allows to use an overloaded callable for the error as well as the valid result path.

Parameters
callback The callback which is used to process the current asynchronous result and error on arrival.
executor The optional executor which is used to dispatch the callback. See the description in then above.
Returns Returns a continuable_base with an asynchronous return type depending on the current result type.
struct my_callable {
  void operator() (std::string result) {
    // ...
  }
  void operator() (cti::exception_arg_t, cti::exception_t) {
    // ...
  }

// Will receive errors and results
http_request("github.com")
  .next(my_callable{});

template<typename Data, typename Annotation> template<typename... Args>
auto cti::continuable_base<Data, Annotation>::as() &&

Returns a continuable_base which will have its signature converted to the given Args.

Returns Returns a continuable_base with an asynchronous return type matching the given Args.

A signature can only be converted if it can be partially applied from the previous one as shown below: continuable<long> c = make_ready_continuable(0, 1, 2).as<long>();

template<typename Data, typename Annotation> template<typename T>
auto cti::continuable_base<Data, Annotation>::apply(T&& transform) &&

A method which allows to apply a callable object to this continuable.

Parameters
transform A callable objects that transforms a continuable to a different object.
Returns Returns the result of the given transform when this continuable is passed into it.

template<typename Data, typename Annotation> template<typename T>
auto cti::continuable_base<Data, Annotation>::operator|(T&& right) &&

The pipe operator | is an alias for the continuable::then method.

Parameters
right The argument on the right-hand side to connect.
Returns See the corresponding continuable_base::then method for the explanation of the return type.

template<typename Data, typename Annotation> template<typename OData, typename OAnnotation>
auto cti::continuable_base<Data, Annotation>::operator&&(continuable_base<OData, OAnnotation>&& right) &&

Invokes both continuable_base objects parallel and calls the callback with the result of both continuable_base objects.

Parameters
right The continuable on the right-hand side to connect.
Returns

Returns a continuable_base with a result type matching the result of the left continuable_base combined with the right continuable_base. The returned continuable_base will be in an intermediate lazy state, further calls to its continuable_base::operator && will add other continuable_base objects to the current invocation chain.

(http_request("github.com") && http_request("atom.io"))
  .then([](std::string github, std::string atom) {
    // ...
  });

auto request = http_request("github.com") && http_request("atom.io");
(std::move(request) && http_request("travis-ci.org"))
   // All three requests are invoked in parallel although we added
   // the request to "travis-ci.org" last.
  .then([](std::string github, std::string atom, std::string travis) {
    // ...
  });

template<typename Data, typename Annotation> template<typename OData, typename OAnnotation>
auto cti::continuable_base<Data, Annotation>::operator||(continuable_base<OData, OAnnotation>&& right) &&

Invokes both continuable_base objects parallel and calls the callback once with the first result available.

Parameters
right The continuable on the right-hand side to connect. The right continuable is required to have the same result as the left connected continuable_base.
Returns

Returns a continuable_base with a result type matching the combined result which of all connected continuable_base objects. The returned continuable_base will be in an intermediate lazy state, further calls to its continuable_base::operator || will add other continuable_base objects to the current invocation chain.

(http_request("github.com") || http_request("atom.io"))
  .then([](std::string github_or_atom) {
    // ...
  });

(make_ready_continuable(10, 'A') || make_ready_continuable(29, 'B'))
  .then([](int a, char b) {
    // ...
  });

template<typename Data, typename Annotation> template<typename OData, typename OAnnotation>
auto cti::continuable_base<Data, Annotation>::operator>>(continuable_base<OData, OAnnotation>&& right) &&

Invokes both continuable_base objects sequential and calls the callback with the result of both continuable_base objects.

Parameters
right The continuable on the right-hand side to connect.
Returns

Returns a continuable_base with a result type matching the result of the left continuable_base combined with the right continuable_base.

(http_request("github.com") >> http_request("atom.io"))
  .then([](std::string github, std::string atom) {
    // The callback is called with the result of both requests,
    // however, the request to atom was started after the request
    // to github was finished.
  });

template<typename Data, typename Annotation>
void cti::continuable_base<Data, Annotation>::done() &&

Invokes the continuation chain manually even before the cti::continuable_base is destructed. This will release the object.

template<typename Data, typename Annotation>
auto cti::continuable_base<Data, Annotation>::finish() &&

Materializes the continuation expression template and finishes the current applied strategy such that the resulting continuable will always be a concrete type and Continuable::is_concrete holds.

This can be used in the case where we are chaining continuations lazily through a strategy, for instance when applying operators for expressing connections and then want to return a materialized continuable_base which uses the strategy respectively.

auto do_both() {
  return (wait(10s) || wait_key_pressed(KEY_SPACE)).finish();
}

// Without a call to finish() this would lead to
// an unintended evaluation strategy:
do_both() || wait(5s);

template<typename Data, typename Annotation>
bool cti::continuable_base<Data, Annotation>::is_ready() const noexcept

Returns true when the continuable can provide its result immediately, and its lazy invocation would be side-effect free.

template<typename Data, typename Annotation>
auto cti::continuable_base<Data, Annotation>::unpack() &&

Invalidates the continuable and returns its immediate invocation result.

Returns A result<Args...> where Args... represent the current asynchronous parameters or the currently stored exception.

This method can be used to specialize the asynchronous control flow based on whether the continuable_base is_ready at every time, which is true for a continuable created through the following functions:

  • make_ready_continuable
  • make_exceptional_continuable

template<typename Data, typename Annotation>
bool cti::continuable_base<Data, Annotation>::is_frozen() const noexcept

Predicate to check whether the cti::continuable_base is frozen or not.

Returns Returns true when the continuable_base is frozen.

template<typename Data, typename Annotation>
continuable_base& cti::continuable_base<Data, Annotation>::freeze(bool enabled = true) &noexcept

Prevents the automatic invocation of the continuation chain which happens on destruction of the continuable_base. You may still invoke the chain through the continuable_base::done method.

Parameters
enabled Indicates whether the freeze is enabled or disabled.

This is useful for storing a continuable_base inside a continuation chain while storing it for further usage.

template<typename Data, typename Annotation>
continuable_base&& cti::continuable_base<Data, Annotation>::freeze(bool enabled = true) &&noexcept

Prevents the automatic invocation of the continuation chain which happens on destruction of the continuable_base. You may still invoke the chain through the continuable_base::done method.

Parameters
enabled Indicates whether the freeze is enabled or disabled.

This is useful for storing a continuable_base inside a continuation chain while storing it for further usage.

template<typename Data, typename Annotation>
auto cti::continuable_base<Data, Annotation>::operator co_await() &&

Implements the operator for awaiting on continuables using co_await.

The operator is only enabled if CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE is defined and the toolchain supports experimental coroutines.

The return type of the co_await expression is specified as following:

Continuation typeco_await returns
continuable_base with <>void
continuable_base with <Arg>Arg
continuable_base with <Args...>std::tuple<Args...>

When exceptions are used the usage is as intuitive as shown below:

// Handling the exception isn't required and
// the try catch clause may be omitted.
try {
  std::string response = co_await http_request("github.com");
} (std::exception& e) {
  e.what();
}

In case the library is configured to use error codes or a custom exception type the return type of the co_await expression is changed. The result is returned through a cti::result<...>.

Continuation typeco_await returns
continuable_base with <>result<void>
continuable_base with <Arg>result<Arg>
continuable_base with <Args...>result<Args...>