/Ratatoskr-cpp

A compact Functional/Reactive/Concurrent utility library on C++17. (will be)

Primary LanguageC++

Ratatoskr A compact Functional/Reactive/Concurrent utility library on C++17.

inline namespace rat::functional

class template thunk<F>

A class template that provides function composition such as map and filter. Template parameter F is used for inner structure, and normally you don't have to care about it. (Thanks to type or template parameter deducition.)

method signature description
<constructor> () -> thunk<void> thunk{} constructs an empty thunk: thunk<void>.
(const thunk<F> &) default;
(thunk<F> &&) default;
map (G &/&&g) -> thunk<H> Return a new thunk that forward the invoke result of this thunk to a functor g. g must return something. (See then.)
filter (G &/&&g) -> thunk<H> Return a new thunk that immediately return std::nullopt if g(<invoke result of this thunk>) == false. Otherwise, the thunk return the result of this thunk.
then (G &/&&g) -> thunk<H> Return a new thunk, that call functor g by the invoke result of this thunk and then return the invoke result of this thunk. It's similar to map, but used to map side effect that returns void.
try_map (G &/&&g) -> thunk<H> g must return std::optional. Return a new thunk that immediately return std::nullopt if g(<invoke result of this thunk>) == std::nullopt. Otherwise, the thunk return the *g(<invoke result of this thunk>).
operator() (T &/&&x) -> std::optional<R> Invoke the composed function by an argument x, then return the result wrapped in std::optional. (Because the result can be filtered and then be none.)

Note: In this page, universal references are referred like as T &/&&.

example:

auto is_even = [](auto n) { return n % 2 == 0; };


auto f = rat::thunk{}
           .filter(is_even)
           .then([](auto n) {
              cout << "even: " << n << endl;
           })
           .map([](auto n) { return n / 2; })
           .filter([](auto n) { return n > 5; });

f(10); // Print "even: 10", then return std::nullopt.
f(11); // Return std::nullopt.
f(12); // Print "even: 12", then return std::optional{6}.

class template bundle<Fs...>

A class template that bundle multiple functions. (thunk combines functions in series, on the other hand, bundle combines functions in parallel.)

method signature description
<constructor> (Fs... fs) -> bundle<Fs...> Bundle multiple functions.
(const bundle<Fs...> &) default;
(bundle<Fs...> &&) default;
bundle_with (Gs &/&&... gs) -> bundle<Hs...> Return new bundle that is concatenated this bundle and functors gs.... If gs... contains some bundles, the bundles will be discomposed and rebundled.
to_tuple () -> std::tuple<Fs...> Convert this bundle to tuple.
as_tuple () & -> std::tuple<Fs...>& Get reference to this bundle as reference to tuple.
() && -> std::tuple<Fs...>&&
operator() (Ts &/&&... xs) -> void Call each functions that this bundle contains one by one in order from top with arguments xs....
results_for (Ts &/&&... xs) -> std::tuple<Rs...> Forward the result of invoking each functions by arguments xs... as tuple.There is no gurantee for invoke order.

inline namespace rat::concurrent

class template channel<T>

A class template that provides inter-thread communication. A channel can have multiple senders and only one receiver.

method signature description
get_receiver () -> receiver<T> Get the receiver. If the receiver is already got, throw rat::concurrent::receiver_already_retrieved.If the channel is closed, throw rat::concurrent::channel_already_closed.
get_sender () -> sender<T> Get the sender. If the channel is closed, throw rat::concurrent::channel_already_closed.
push (const T &x) -> void Send a value to the channel then notify the receiver.
(T &&x) -> void
close () -> void Close the channel then notify the receiver.

class template sender<T>

You can get it by channel<T>::get_sender().

method signature description
push (const T &x) -> void Send a value to the channel then notify the receiver.
(T &&x) -> void
close () -> void Close the channel then notify the receiver.

class template receiver<T>

You can get it by channel<T>::get_receiver(). The receiver is non-copyable but moveable.

method signature description
next () -> T Take the value sent to the channel one by one as same order as it was sent.If the channel is empty, block the thread until new value is sent.When the channel is closed, throw rat::concurrent::close_channel.
share () -> shared_receiver<T> Return shared_receiver and invalidate this receiver.

class template shared_receiver<T>

The shared_receiver is a copyable receiver.Even if there are multiple shared_receiver, one sent value will received only one time on only one shared_receiver.It is useful for processing sent value on multiple threads.

method signature description
next () -> T Take the value sent to the channel one by one as same order as it was sent.If the channel is empty, block the thread until new value is sent.When the channel is closed, throw rat::concurrent::close_channel.

helper function

function signature description
make_channel<T> () -> std::pair<sender<T>, receiver<T>> Create a pair of sender<T> and receiver<T>.
(with_shared_receiver_t) -> std::pair<sender<T>, shared_receiver<T>> When passed with_shared_receiver, share the receiver.

example:

using namespace std::chrono_literals;

auto [sn, rc] = rat::make_channel<int>();

auto produce = [](auto sn) {
  for (int i = 0; i < 10; ++i) {
    sn.push(i);
    std::this_thread::sleep_for(1s);
  }
  sn.close();
};

auto consume = [](auto rc) {
  try {
    while (true) {
      std::cout << rc.next() << std::endl;
    }
  }
  catch (const close_channel &) {
  }
};

std::thread producer{produce, std::move(sn)};
std::thread consumer{consume, std::move(rc)};
producer.join();
consumer.join();