673 lines
16 KiB
C++
673 lines
16 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#pragma once
|
|
|
|
#include "base/lambda.h"
|
|
#include <rpl/consumer.h>
|
|
#include <rpl/lifetime.h>
|
|
#include <rpl/details/superset_type.h>
|
|
#include <rpl/details/callable.h>
|
|
|
|
namespace rpl {
|
|
namespace details {
|
|
|
|
template <typename Value, typename Error>
|
|
const consumer<Value, Error> &const_ref_consumer();
|
|
|
|
template <typename Lambda>
|
|
class mutable_lambda_wrap {
|
|
public:
|
|
mutable_lambda_wrap(Lambda &&lambda)
|
|
: _lambda(std::move(lambda)) {
|
|
}
|
|
mutable_lambda_wrap(const mutable_lambda_wrap &other) = default;
|
|
mutable_lambda_wrap(mutable_lambda_wrap &&other) = default;
|
|
mutable_lambda_wrap &operator=(
|
|
const mutable_lambda_wrap &other) = default;
|
|
mutable_lambda_wrap &operator=(
|
|
mutable_lambda_wrap &&other) = default;
|
|
|
|
template <typename... Args>
|
|
auto operator()(Args&&... args) const {
|
|
return (const_cast<mutable_lambda_wrap*>(this)->_lambda)(
|
|
std::forward<Args>(args)...);
|
|
}
|
|
|
|
private:
|
|
Lambda _lambda;
|
|
|
|
};
|
|
|
|
// Type-erased copyable mutable lambda using base::lambda.
|
|
template <typename Value, typename Error>
|
|
class type_erased_generator final {
|
|
public:
|
|
template <typename Handlers>
|
|
using consumer_type = consumer<Value, Error, Handlers>;
|
|
using value_type = Value;
|
|
using error_type = Error;
|
|
|
|
type_erased_generator(
|
|
const type_erased_generator &other) = default;
|
|
type_erased_generator(
|
|
type_erased_generator &&other) = default;
|
|
type_erased_generator &operator=(
|
|
const type_erased_generator &other) = default;
|
|
type_erased_generator &operator=(
|
|
type_erased_generator &&other) = default;
|
|
|
|
template <
|
|
typename Generator,
|
|
typename = std::enable_if_t<
|
|
std::is_convertible_v<
|
|
decltype(std::declval<Generator>()(
|
|
const_ref_consumer<Value, Error>())),
|
|
lifetime> &&
|
|
!std::is_same_v<
|
|
std::decay_t<Generator>,
|
|
type_erased_generator>>>
|
|
type_erased_generator(Generator other) : _implementation(
|
|
mutable_lambda_wrap<Generator>(std::move(other))) {
|
|
}
|
|
template <
|
|
typename Generator,
|
|
typename = std::enable_if_t<
|
|
std::is_convertible_v<
|
|
decltype(std::declval<Generator>()(
|
|
const_ref_consumer<Value, Error>())),
|
|
lifetime> &&
|
|
!std::is_same_v<
|
|
std::decay_t<Generator>,
|
|
type_erased_generator>>>
|
|
type_erased_generator &operator=(Generator other) {
|
|
_implementation = mutable_lambda_wrap<Generator>(
|
|
std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
template <typename Handlers>
|
|
lifetime operator()(const consumer_type<Handlers> &consumer) {
|
|
return _implementation(consumer);
|
|
}
|
|
|
|
private:
|
|
base::lambda<lifetime(const consumer_type<type_erased_handlers<Value, Error>> &)> _implementation;
|
|
|
|
};
|
|
|
|
} // namespace details
|
|
|
|
template <
|
|
typename Value = empty_value,
|
|
typename Error = no_error,
|
|
typename Generator = details::type_erased_generator<
|
|
Value,
|
|
Error>>
|
|
class producer;
|
|
|
|
template <
|
|
typename Value1,
|
|
typename Value2,
|
|
typename Error1,
|
|
typename Error2,
|
|
typename Generator>
|
|
struct superset_type<
|
|
producer<Value1, Error1, Generator>,
|
|
producer<Value2, Error2, Generator>> {
|
|
using type = producer<
|
|
superset_type_t<Value1, Value2>,
|
|
superset_type_t<Error1, Error2>,
|
|
Generator>;
|
|
};
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator1,
|
|
typename Generator2>
|
|
struct superset_type<
|
|
producer<Value, Error, Generator1>,
|
|
producer<Value, Error, Generator2>> {
|
|
using type = producer<Value, Error>;
|
|
};
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator>
|
|
struct superset_type<
|
|
producer<Value, Error, Generator>,
|
|
producer<Value, Error, Generator>> {
|
|
using type = producer<Value, Error, Generator>;
|
|
};
|
|
|
|
namespace details {
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
class producer_base {
|
|
public:
|
|
template <typename Handlers>
|
|
using consumer_type = consumer<Value, Error, Handlers>;
|
|
using value_type = Value;
|
|
using error_type = Error;
|
|
|
|
template <
|
|
typename OtherGenerator,
|
|
typename = std::enable_if_t<
|
|
std::is_constructible_v<Generator, OtherGenerator&&>>>
|
|
producer_base(OtherGenerator &&generator);
|
|
|
|
producer_base(const producer_base &other) = default;
|
|
producer_base(producer_base &&other) = default;
|
|
producer_base &operator=(const producer_base &other) = default;
|
|
producer_base &operator=(producer_base &&other) = default;
|
|
|
|
template <
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename = std::enable_if_t<
|
|
is_callable_v<OnNext, Value>
|
|
&& is_callable_v<OnError, Error>
|
|
&& is_callable_v<OnDone>>>
|
|
lifetime start(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done) &&;
|
|
|
|
template <
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename = std::enable_if_t<
|
|
is_callable_v<OnNext, Value>
|
|
&& is_callable_v<OnError, Error>
|
|
&& is_callable_v<OnDone>>>
|
|
lifetime start_copy(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done) const &;
|
|
|
|
template <typename Handlers>
|
|
lifetime start_existing(
|
|
const consumer_type<Handlers> &consumer) &&;
|
|
|
|
private:
|
|
Generator _generator;
|
|
|
|
template <
|
|
typename OtherValue,
|
|
typename OtherError,
|
|
typename OtherGenerator>
|
|
friend class ::rpl::producer;
|
|
|
|
};
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
template <typename OtherGenerator, typename>
|
|
inline producer_base<Value, Error, Generator>::producer_base(
|
|
OtherGenerator &&generator)
|
|
: _generator(std::forward<OtherGenerator>(generator)) {
|
|
}
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
template <
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename>
|
|
inline lifetime producer_base<Value, Error, Generator>::start(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done) && {
|
|
return std::move(*this).start_existing(make_consumer<Value, Error>(
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnError>(error),
|
|
std::forward<OnDone>(done)));
|
|
}
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
template <
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename>
|
|
inline lifetime producer_base<Value, Error, Generator>::start_copy(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done) const & {
|
|
auto copy = *this;
|
|
return std::move(copy).start(
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnError>(error),
|
|
std::forward<OnDone>(done));
|
|
}
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
template <typename Handlers>
|
|
inline lifetime producer_base<Value, Error, Generator>::start_existing(
|
|
const consumer_type<Handlers> &consumer) && {
|
|
if (consumer.add_lifetime(std::move(_generator)(consumer))) {
|
|
return [consumer] { consumer.terminate(); };
|
|
}
|
|
return lifetime();
|
|
}
|
|
|
|
template <typename Value, typename Error>
|
|
using producer_base_type_erased = producer_base<
|
|
Value,
|
|
Error,
|
|
type_erased_generator<Value, Error>>;
|
|
|
|
} // namespace details
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
class producer final
|
|
: public details::producer_base<Value, Error, Generator> {
|
|
using parent_type = details::producer_base<
|
|
Value,
|
|
Error,
|
|
Generator>;
|
|
|
|
public:
|
|
using parent_type::parent_type;
|
|
|
|
};
|
|
|
|
template <typename Value, typename Error>
|
|
class producer<
|
|
Value,
|
|
Error,
|
|
details::type_erased_generator<Value, Error>> final
|
|
: public details::producer_base_type_erased<Value, Error> {
|
|
using parent_type = details::producer_base_type_erased<
|
|
Value,
|
|
Error>;
|
|
|
|
public:
|
|
using parent_type::parent_type;;
|
|
|
|
producer(const producer &other) = default;
|
|
producer(producer &&other) = default;
|
|
producer &operator=(const producer &other) = default;
|
|
producer &operator=(producer &&other) = default;
|
|
|
|
template <
|
|
typename Generic,
|
|
typename = std::enable_if_t<!std::is_same_v<
|
|
Generic,
|
|
details::type_erased_generator<Value, Error>>>>
|
|
producer(const details::producer_base<Value, Error, Generic> &other)
|
|
: parent_type(other._generator) {
|
|
}
|
|
|
|
template <
|
|
typename Generic,
|
|
typename = std::enable_if_t<!std::is_same_v<
|
|
Generic,
|
|
details::type_erased_generator<Value, Error>>>>
|
|
producer(details::producer_base<Value, Error, Generic> &&other)
|
|
: parent_type(std::move(other._generator)) {
|
|
}
|
|
|
|
template <
|
|
typename Generic,
|
|
typename = std::enable_if_t<!std::is_same_v<
|
|
Generic,
|
|
details::type_erased_generator<Value, Error>>>>
|
|
producer &operator=(
|
|
const details::producer_base<Value, Error, Generic> &other) {
|
|
this->_generator = other._generator;
|
|
return *this;
|
|
}
|
|
|
|
template <
|
|
typename Generic,
|
|
typename = std::enable_if_t<!std::is_same_v<
|
|
Generic,
|
|
details::type_erased_generator<Value, Error>>>>
|
|
producer &operator=(
|
|
details::producer_base<Value, Error, Generic> &&other) {
|
|
this->_generator = std::move(other._generator);
|
|
return *this;
|
|
}
|
|
|
|
};
|
|
|
|
template <
|
|
typename Value = empty_value,
|
|
typename Error = no_error,
|
|
typename Generator,
|
|
typename = std::enable_if_t<
|
|
std::is_convertible_v<
|
|
decltype(std::declval<Generator>()(
|
|
details::const_ref_consumer<Value, Error>())),
|
|
lifetime>>>
|
|
inline auto make_producer(Generator &&generator)
|
|
-> producer<Value, Error, std::decay_t<Generator>> {
|
|
return std::forward<Generator>(generator);
|
|
}
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
inline producer<Value, Error, Generator> duplicate(
|
|
const producer<Value, Error, Generator> &value) {
|
|
return value;
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename Method,
|
|
typename = decltype(std::declval<Method>()(
|
|
std::declval<producer<Value, Error, Generator>>()))>
|
|
inline auto operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
Method &&method) {
|
|
return std::forward<Method>(method)(std::move(value));
|
|
}
|
|
|
|
namespace details {
|
|
|
|
struct lifetime_with_none {
|
|
lifetime &alive_while;
|
|
};
|
|
|
|
template <typename OnNext>
|
|
struct lifetime_with_next {
|
|
lifetime &alive_while;
|
|
OnNext next;
|
|
};
|
|
|
|
template <typename OnError>
|
|
struct lifetime_with_error {
|
|
lifetime &alive_while;
|
|
OnError error;
|
|
};
|
|
|
|
template <typename OnDone>
|
|
struct lifetime_with_done {
|
|
lifetime &alive_while;
|
|
OnDone done;
|
|
};
|
|
|
|
template <typename OnNext, typename OnError>
|
|
struct lifetime_with_next_error {
|
|
lifetime &alive_while;
|
|
OnNext next;
|
|
OnError error;
|
|
};
|
|
|
|
template <typename OnError, typename OnDone>
|
|
struct lifetime_with_error_done {
|
|
lifetime &alive_while;
|
|
OnError error;
|
|
OnDone done;
|
|
};
|
|
|
|
template <typename OnNext, typename OnDone>
|
|
struct lifetime_with_next_done {
|
|
lifetime &alive_while;
|
|
OnNext next;
|
|
OnDone done;
|
|
};
|
|
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
struct lifetime_with_next_error_done {
|
|
lifetime &alive_while;
|
|
OnNext next;
|
|
OnError error;
|
|
OnDone done;
|
|
};
|
|
|
|
} // namespace details
|
|
|
|
inline auto start(lifetime &alive_while)
|
|
-> details::lifetime_with_none {
|
|
return { alive_while };
|
|
}
|
|
|
|
template <typename OnNext>
|
|
inline auto start_with_next(OnNext &&next, lifetime &alive_while)
|
|
-> details::lifetime_with_next<std::decay_t<OnNext>> {
|
|
return { alive_while, std::forward<OnNext>(next) };
|
|
}
|
|
|
|
template <typename OnError>
|
|
inline auto start_with_error(OnError &&error, lifetime &alive_while)
|
|
-> details::lifetime_with_error<std::decay_t<OnError>> {
|
|
return { alive_while, std::forward<OnError>(error) };
|
|
}
|
|
|
|
template <typename OnDone>
|
|
inline auto start_with_done(OnDone &&done, lifetime &alive_while)
|
|
-> details::lifetime_with_done<std::decay_t<OnDone>> {
|
|
return { alive_while, std::forward<OnDone>(done) };
|
|
}
|
|
|
|
template <typename OnNext, typename OnError>
|
|
inline auto start_with_next_error(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
lifetime &alive_while)
|
|
-> details::lifetime_with_next_error<
|
|
std::decay_t<OnNext>,
|
|
std::decay_t<OnError>> {
|
|
return {
|
|
alive_while,
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnError>(error)
|
|
};
|
|
}
|
|
|
|
template <typename OnError, typename OnDone>
|
|
inline auto start_with_error_done(
|
|
OnError &&error,
|
|
OnDone &&done,
|
|
lifetime &alive_while)
|
|
-> details::lifetime_with_error_done<
|
|
std::decay_t<OnError>,
|
|
std::decay_t<OnDone>> {
|
|
return {
|
|
alive_while,
|
|
std::forward<OnError>(error),
|
|
std::forward<OnDone>(done)
|
|
};
|
|
}
|
|
|
|
template <typename OnNext, typename OnDone>
|
|
inline auto start_with_next_done(
|
|
OnNext &&next,
|
|
OnDone &&done,
|
|
lifetime &alive_while)
|
|
-> details::lifetime_with_next_done<
|
|
std::decay_t<OnNext>,
|
|
std::decay_t<OnDone>> {
|
|
return {
|
|
alive_while,
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnDone>(done)
|
|
};
|
|
}
|
|
|
|
template <typename OnNext, typename OnError, typename OnDone>
|
|
inline auto start_with_next_error_done(
|
|
OnNext &&next,
|
|
OnError &&error,
|
|
OnDone &&done,
|
|
lifetime &alive_while)
|
|
-> details::lifetime_with_next_error_done<
|
|
std::decay_t<OnNext>,
|
|
std::decay_t<OnError>,
|
|
std::decay_t<OnDone>> {
|
|
return {
|
|
alive_while,
|
|
std::forward<OnNext>(next),
|
|
std::forward<OnError>(error),
|
|
std::forward<OnDone>(done)
|
|
};
|
|
}
|
|
|
|
namespace details {
|
|
|
|
template <typename Value, typename Error, typename Generator>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_none &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
[] {},
|
|
[] {},
|
|
[] {}));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnNext,
|
|
typename = std::enable_if_t<is_callable_v<OnNext, Value>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_next<OnNext> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
std::move(lifetime.next),
|
|
[] {},
|
|
[] {}));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnError,
|
|
typename = std::enable_if_t<is_callable_v<OnError, Error>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_error<OnError> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
[] {},
|
|
std::move(lifetime.error),
|
|
[] {}));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnDone,
|
|
typename = std::enable_if_t<is_callable_v<OnDone>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_done<OnDone> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
[] {},
|
|
[] {},
|
|
std::move(lifetime.done)));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename = std::enable_if_t<
|
|
is_callable_v<OnNext, Value> &&
|
|
is_callable_v<OnError, Error>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_next_error<OnNext, OnError> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
std::move(lifetime.next),
|
|
std::move(lifetime.error),
|
|
[] {}));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename = std::enable_if_t<
|
|
is_callable_v<OnError, Error> &&
|
|
is_callable_v<OnDone>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_error_done<OnError, OnDone> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
[] {},
|
|
std::move(lifetime.error),
|
|
std::move(lifetime.done)));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnNext,
|
|
typename OnDone,
|
|
typename = std::enable_if_t<
|
|
is_callable_v<OnNext, Value> &&
|
|
is_callable_v<OnDone>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_next_done<OnNext, OnDone> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
std::move(lifetime.next),
|
|
[] {},
|
|
std::move(lifetime.done)));
|
|
}
|
|
|
|
template <
|
|
typename Value,
|
|
typename Error,
|
|
typename Generator,
|
|
typename OnNext,
|
|
typename OnError,
|
|
typename OnDone,
|
|
typename = std::enable_if_t<
|
|
is_callable_v<OnNext, Value> &&
|
|
is_callable_v<OnError, Error> &&
|
|
is_callable_v<OnDone>>>
|
|
inline void operator|(
|
|
producer<Value, Error, Generator> &&value,
|
|
lifetime_with_next_error_done<
|
|
OnNext,
|
|
OnError,
|
|
OnDone> &&lifetime) {
|
|
lifetime.alive_while.add(
|
|
std::move(value).start(
|
|
std::move(lifetime.next),
|
|
std::move(lifetime.error),
|
|
std::move(lifetime.done)));
|
|
}
|
|
|
|
} // namespace details
|
|
} // namespace rpl
|