Improve rpl::combine() and rpl::start().

This commit is contained in:
John Preston 2017-09-18 19:39:45 +03:00
parent 5e7aa4ff81
commit ee9763c98f
26 changed files with 2119 additions and 403 deletions

View File

@ -288,8 +288,9 @@ void EditPrivacyBox::createWidgets() {
auto createExceptionLink = [this](Exception exception) {
exceptionLink(exception).create(this, object_ptr<Ui::LinkButton>(this, exceptionLinkText(exception)), exceptionLinkMargins());
exceptionLink(exception)->heightValue()
| rpl::on_next([this](int) { resizeToWidth(width()); })
| rpl::start(lifetime());
| rpl::start(
[this](int) { resizeToWidth(width()); },
lifetime());
exceptionLink(exception)->entity()->setClickedCallback([this, exception] { editExceptionUsers(exception); });
};

View File

@ -107,7 +107,7 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget(
| rpl::on_next([this, inner = _inner](int value) {
inner->setVisibleTopBottom(
value,
value + _scroll->height()); // TODO rpl::combine_latest
value + _scroll->height()); // TODO rpl::combine
})
| rpl::start(_inner->lifetime());
return _inner;

View File

@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_profile_inner_widget.h"
#include <rpl/combine_latest.h>
#include <rpl/combine.h>
#include "boxes/abstract_box.h"
#include "mainwidget.h"
#include "info/info_profile_widget.h"
@ -177,8 +177,7 @@ object_ptr<Ui::RpWidget> InnerWidget::setupInfoLines(
result,
object_ptr<Ui::PlainShadow>(result, st::shadowFg),
st::infoProfileSeparatorPadding));
rpl::combine_latest(std::move(infoPartsShown))
| rpl::map([](const std::vector<bool> &values) {
rpl::combine(std::move(infoPartsShown), [](const auto &values) {
return base::find(values, true) != values.end();
})
| rpl::distinct_until_changed()

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/filter.h>
#include <rpl/never.h>
#include <rpl/before_next.h>
#include <rpl/combine_latest.h>
#include <rpl/combine.h>
#include "styles/style_info.h"
#include "profile/profile_userpic_button.h"
#include "observer_peer.h"
@ -182,15 +182,11 @@ rpl::producer<bool> CanShareContactViewer(
rpl::producer<bool> CanAddContactViewer(
not_null<UserData*> user) {
return rpl::combine_latest(
using namespace rpl::mappers;
return rpl::combine(
IsContactViewer(user),
CanShareContactViewer(user))
| rpl::map([](auto &&value) {
return !std::get<0>(value) && std::get<1>(value);
})
| rpl::map_error([](auto &&error) {
return *base::get_if<rpl::no_error>(&error);
});
CanShareContactViewer(user),
!$1 && $2);
}
FloatingIcon::FloatingIcon(

View File

@ -26,7 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace rpl {
template <typename SideEffect>
auto before_next(SideEffect &&method) {
inline auto before_next(SideEffect &&method) {
return filter([method = std::forward<SideEffect>(method)](
const auto &value) {
method(value);

View File

@ -0,0 +1,333 @@
/*
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 <rpl/producer.h>
#include <rpl/details/type_list.h>
#include <rpl/details/callable.h>
#include <rpl/mappers.h>
#include <rpl/complete.h>
namespace rpl {
namespace details {
template <typename ...Values>
struct combine_state {
combine_state() : accumulated(std::tuple<base::optional<Values>...>()) {
}
base::optional<std::tuple<base::optional<Values>...>> accumulated;
base::optional<std::tuple<Values...>> latest;
int invalid = sizeof...(Values);
int working = sizeof...(Values);
};
template <typename ...Values, std::size_t ...I>
inline std::tuple<Values...> combine_make_first(
std::tuple<base::optional<Values>...> &&accumulated,
std::index_sequence<I...>) {
return std::make_tuple(std::move(*std::get<I>(accumulated))...);
}
template <size_t Index, typename consumer_type, typename ...Values>
class combine_subscribe_one {
public:
combine_subscribe_one(
const consumer_type &consumer,
combine_state<Values...> *state)
: _consumer(consumer)
, _state(state) {
}
template <typename Value, typename Error>
void subscribe(producer<Value, Error> &&producer) {
_consumer.add_lifetime(std::move(producer).start(
[consumer = _consumer, state = _state](Value &&value) {
if (!state->accumulated) {
std::get<Index>(*state->latest) = std::move(value);
consumer.put_next_copy(*state->latest);
} else {
auto &accumulated = std::get<Index>(
*state->accumulated);
if (accumulated) {
accumulated = std::move(value);
} else {
accumulated = std::move(value);
if (!--state->invalid) {
constexpr auto kArity = sizeof...(Values);
state->latest = combine_make_first(
std::move(*state->accumulated),
std::make_index_sequence<kArity>());
state->accumulated = base::none;
consumer.put_next_copy(*state->latest);
}
}
}
}, [consumer = _consumer](Error &&error) {
consumer.put_error(std::move(error));
}, [consumer = _consumer, state = _state] {
if (!--state->working) {
consumer.put_done();
}
}));
}
private:
const consumer_type &_consumer;
combine_state<Values...> *_state = nullptr;
};
template <
typename consumer_type,
typename ...Values,
typename ...Errors,
std::size_t ...I>
inline void combine_subscribe(
const consumer_type &consumer,
combine_state<Values...> *state,
std::index_sequence<I...>,
producer<Values, Errors> &&...producers) {
auto consume = { (
details::combine_subscribe_one<
I,
consumer_type,
Values...
>(
consumer,
state
).subscribe(std::move(producers)), 0)... };
(void)consume;
}
template <typename ...Values, typename ...Errors>
inline auto combine_implementation(
producer<Values, Errors> &&...producers) {
using CombinedError = details::normalized_variant_t<Errors...>;
using Result = producer<
std::tuple<Values...>,
CombinedError>;
using consumer_type = typename Result::consumer_type;
auto result = [](
const consumer_type &consumer,
producer<Values, Errors> &...producers) {
auto state = consumer.template make_state<
details::combine_state<Values...>>();
constexpr auto kArity = sizeof...(Values);
details::combine_subscribe(
consumer,
state,
std::make_index_sequence<kArity>(),
std::move(producers)...);
return lifetime();
};
return Result(std::bind(
result,
std::placeholders::_1,
std::move(producers)...));
}
template <typename ...Args>
struct combine_just_producers : std::false_type {
};
template <typename ...Args>
constexpr bool combine_just_producers_v
= combine_just_producers<Args...>::value;
template <typename ...Values, typename ...Errors>
struct combine_just_producers<producer<Values, Errors>...>
: std::true_type {
};
template <typename ArgsList>
struct combine_just_producers_list
: type_list::extract_to_t<ArgsList, combine_just_producers> {
};
template <typename ...Args>
struct combine_result_type;
template <typename ...Args>
using combine_result_type_t
= typename combine_result_type<Args...>::type;
template <typename ...Values, typename ...Errors>
struct combine_result_type<producer<Values, Errors>...> {
using type = std::tuple<Values...>;
};
template <typename ArgsList>
struct combine_result_type_list
: type_list::extract_to_t<ArgsList, combine_result_type> {
};
template <typename ArgsList>
using combine_result_type_list_t
= typename combine_result_type_list<ArgsList>::type;
template <typename ArgsList>
using combine_producers_no_mapper_t
= type_list::chop_last_t<ArgsList>;
template <typename ArgsList>
constexpr bool combine_is_good_mapper(std::true_type) {
return is_callable_v<
type_list::last_t<ArgsList>,
combine_result_type_list_t<
combine_producers_no_mapper_t<ArgsList>
>>;
}
template <typename ArgsList>
constexpr bool combine_is_good_mapper(std::false_type) {
return false;
}
template <typename ArgsList>
struct combine_producers_with_mapper_list : std::bool_constant<
combine_is_good_mapper<ArgsList>(
combine_just_producers_list<
combine_producers_no_mapper_t<ArgsList>
>())> {
};
template <typename ...Args>
struct combine_producers_with_mapper
: combine_producers_with_mapper_list<type_list::list<Args...>> {
};
template <typename ...Args>
constexpr bool combine_producers_with_mapper_v
= combine_producers_with_mapper<Args...>::value;
template <typename ...Values, typename ...Errors>
inline decltype(auto) combine_helper(
std::true_type,
producer<Values, Errors> &&...producers) {
return combine_implementation(std::move(producers)...);
}
template <typename ...Producers, std::size_t ...I>
inline decltype(auto) combine_call(
std::index_sequence<I...>,
Producers &&...producers) {
return combine_implementation(
argument_mapper<I>::call(std::move(producers)...)...);
}
template <typename ...Args>
inline decltype(auto) combine_helper(
std::false_type,
Args &&...args) {
constexpr auto kProducersCount = sizeof...(Args) - 1;
return combine_call(
std::make_index_sequence<kProducersCount>(),
std::forward<Args>(args)...)
| map(argument_mapper<kProducersCount>::call(
std::forward<Args>(args)...));
}
} // namespace details
template <
typename ...Args,
typename = std::enable_if_t<
details::combine_just_producers_v<Args...>
|| details::combine_producers_with_mapper_v<Args...>>>
inline decltype(auto) combine(Args &&...args) {
return details::combine_helper(
details::combine_just_producers<Args...>(),
std::forward<Args>(args)...);
}
namespace details {
template <typename Value>
struct combine_vector_state {
std::vector<base::optional<Value>> accumulated;
std::vector<Value> latest;
int invalid = 0;
int working = 0;
};
} // namespace details
template <typename Value, typename Error>
inline producer<std::vector<Value>, Error> combine(
std::vector<producer<Value, Error>> &&producers) {
if (producers.empty()) {
return complete<std::vector<Value>, Error>();
}
using state_type = details::combine_vector_state<Value>;
using consumer_type = consumer<std::vector<Value>, Error>;
return [producers = std::move(producers)](
const consumer_type &consumer) mutable {
auto count = producers.size();
auto state = consumer.template make_state<state_type>();
state->accumulated.resize(count);
state->invalid = count;
state->working = count;
for (auto index = 0; index != count; ++index) {
auto &producer = producers[index];
consumer.add_lifetime(std::move(producer).start(
[consumer, state, index](Value &&value) {
if (state->accumulated.empty()) {
state->latest[index] = std::move(value);
consumer.put_next_copy(state->latest);
} else if (state->accumulated[index]) {
state->accumulated[index] = std::move(value);
} else {
state->accumulated[index] = std::move(value);
if (!--state->invalid) {
state->latest.reserve(
state->accumulated.size());
for (auto &&value : state->accumulated) {
state->latest.push_back(
std::move(*value));
}
base::take(state->accumulated);
consumer.put_next_copy(state->latest);
}
}
}, [consumer](Error &&error) {
consumer.put_error(std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
}
return lifetime();
};
}
template <typename Value, typename Error, typename Mapper>
inline auto combine(
std::vector<producer<Value, Error>> &&producers,
Mapper &&mapper) {
return combine(std::move(producers))
| map(std::forward<Mapper>(mapper));
}
} // namespace rpl

View File

@ -1,155 +0,0 @@
/*
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 <rpl/producer.h>
#include <rpl/complete.h>
#include "base/optional.h"
namespace rpl {
namespace details {
template <typename Value>
struct combine_latest_vector_state {
std::vector<base::optional<Value>> accumulated;
std::vector<Value> latest;
int invalid = 0;
int working = 0;
};
} // namespace details
template <typename Value, typename Error>
producer<std::vector<Value>, Error> combine_latest(
std::vector<producer<Value, Error>> &&producers) {
if (producers.empty()) {
return complete<std::vector<Value>, Error>();
}
using state_type = details::combine_latest_vector_state<Value>;
using consumer_type = consumer<std::vector<Value>, Error>;
return [producers = std::move(producers)](
const consumer_type &consumer) mutable {
auto count = producers.size();
auto state = consumer.make_state<state_type>();
state->accumulated.resize(count);
state->invalid = count;
state->working = count;
for (auto index = 0; index != count; ++index) {
auto &producer = producers[index];
consumer.add_lifetime(std::move(producer).start(
[consumer, state, index](Value &&value) {
if (state->accumulated.empty()) {
state->latest[index] = std::move(value);
consumer.put_next_copy(state->latest);
} else if (state->accumulated[index]) {
state->accumulated[index] = std::move(value);
} else {
state->accumulated[index] = std::move(value);
if (!--state->invalid) {
state->latest.reserve(
state->accumulated.size());
for (auto &&value : state->accumulated) {
state->latest.push_back(
std::move(*value));
}
base::take(state->accumulated);
consumer.put_next_copy(state->latest);
}
}
}, [consumer](Error &&error) {
consumer.put_error(std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
}
return lifetime();
};
}
namespace details {
template <typename Value, typename ...Values>
struct combine_latest_tuple_state {
base::optional<Value> first;
base::optional<std::tuple<Values...>> others;
int working = 2;
};
} // namespace details
template <
typename Value,
typename Error,
typename ...Values,
typename ...Errors>
producer<std::tuple<Value, Values...>, base::variant<Error, Errors...>> combine_latest(
producer<Value, Error> &&first,
producer<Values, Errors> &&...others) {
auto others_combined = combine_latest(std::move(others)...);
return [
first = std::move(first),
others = std::move(others_combined)
](const consumer<std::tuple<Value, Values...>, base::variant<Error, Errors...>> &consumer) mutable {
auto state = consumer.make_state<details::combine_latest_tuple_state<Value, Values...>>();
consumer.add_lifetime(std::move(first).start([consumer, state](Value &&value) {
state->first = std::move(value);
if (state->others) {
consumer.put_next(std::tuple_cat(std::make_tuple(*state->first), *state->others));
}
}, [consumer](Error &&error) {
consumer.put_error(std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
consumer.add_lifetime(std::move(others).start([consumer, state](std::tuple<Values...> &&value) {
state->others = std::move(value);
if (state->first) {
consumer.put_next(std::tuple_cat(std::make_tuple(*state->first), *state->others));
}
}, [consumer](base::variant<Errors...> &&error) {
base::visit([&](auto &&errorValue) {
consumer.put_error(std::move(errorValue));
}, std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
return lifetime();
};
}
template <
typename Value,
typename Error>
producer<std::tuple<Value>, Error> combine_latest(
producer<Value, Error> &&producer) {
return std::move(producer) | map([](auto &&value) {
return std::make_tuple(std::forward<decltype(value)>(value));
});
}
} // namespace rpl

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace rpl {
template <typename Value = empty_value, typename Error = no_error>
producer<Value, Error> complete() {
inline producer<Value, Error> complete() {
return [](const consumer<Value, Error> &consumer) mutable {
consumer.put_done();
return lifetime();

View File

@ -23,30 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <mutex>
#include <gsl/gsl_assert>
#include <rpl/lifetime.h>
#include <rpl/details/callable.h>
namespace rpl {
namespace details {
template <typename Arg>
const Arg &const_ref_val();
template <
typename Func,
typename Arg,
typename = decltype(std::declval<Func>()(const_ref_val<Arg>()))>
void const_ref_call_helper(Func &handler, const Arg &value, int) {
handler(value);
}
template <
typename Func,
typename Arg>
void const_ref_call_helper(Func &handler, const Arg &value, double) {
auto copy = value;
handler(std::move(copy));
}
} // namespace details
struct no_value {
no_value() = delete;
@ -69,8 +48,10 @@ public:
typename OnNext,
typename OnError,
typename OnDone,
typename = decltype(std::declval<OnNext>()(std::declval<Value>())),
typename = decltype(std::declval<OnError>()(std::declval<Error>())),
typename = std::enable_if_t<
details::is_callable_v<OnNext, Value>>,
typename = decltype(std::declval<OnError>()(
std::declval<Error>())),
typename = decltype(std::declval<OnDone>()())>
consumer(
OnNext &&next,
@ -122,24 +103,24 @@ public:
}
private:
class abstract_consumer_instance;
class abstract_instance;
template <typename OnNext, typename OnError, typename OnDone>
class consumer_instance;
class instance;
template <typename OnNext, typename OnError, typename OnDone>
std::shared_ptr<abstract_consumer_instance> ConstructInstance(
std::shared_ptr<abstract_instance> ConstructInstance(
OnNext &&next,
OnError &&error,
OnDone &&done);
mutable std::shared_ptr<abstract_consumer_instance> _instance;
mutable std::shared_ptr<abstract_instance> _instance;
};
template <typename Value, typename Error>
class consumer<Value, Error>::abstract_consumer_instance {
class consumer<Value, Error>::abstract_instance {
public:
virtual bool put_next(Value &&value) = 0;
virtual bool put_next_copy(const Value &value) = 0;
@ -163,11 +144,14 @@ protected:
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
class consumer<Value, Error>::consumer_instance
: public consumer<Value, Error>::abstract_consumer_instance {
class consumer<Value, Error>::instance
: public consumer<Value, Error>::abstract_instance {
public:
template <typename OnNextImpl, typename OnErrorImpl, typename OnDoneImpl>
consumer_instance(
template <
typename OnNextImpl,
typename OnErrorImpl,
typename OnDoneImpl>
instance(
OnNextImpl &&next,
OnErrorImpl &&error,
OnDoneImpl &&done)
@ -191,12 +175,13 @@ private:
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
std::shared_ptr<typename consumer<Value, Error>::abstract_consumer_instance>
inline std::shared_ptr<
typename consumer<Value, Error>::abstract_instance>
consumer<Value, Error>::ConstructInstance(
OnNext &&next,
OnError &&error,
OnDone &&done) {
return std::make_shared<consumer_instance<
return std::make_shared<instance<
std::decay_t<OnNext>,
std::decay_t<OnError>,
std::decay_t<OnDone>>>(
@ -213,7 +198,7 @@ template <
typename,
typename,
typename>
consumer<Value, Error>::consumer(
inline consumer<Value, Error>::consumer(
OnNext &&next,
OnError &&error,
OnDone &&done) : _instance(ConstructInstance(
@ -223,7 +208,7 @@ consumer<Value, Error>::consumer(
}
template <typename Value, typename Error>
bool consumer<Value, Error>::put_next(Value &&value) const {
inline bool consumer<Value, Error>::put_next(Value &&value) const {
if (_instance) {
if (_instance->put_next(std::move(value))) {
return true;
@ -234,7 +219,8 @@ bool consumer<Value, Error>::put_next(Value &&value) const {
}
template <typename Value, typename Error>
bool consumer<Value, Error>::put_next_copy(const Value &value) const {
inline bool consumer<Value, Error>::put_next_copy(
const Value &value) const {
if (_instance) {
if (_instance->put_next_copy(value)) {
return true;
@ -245,28 +231,30 @@ bool consumer<Value, Error>::put_next_copy(const Value &value) const {
}
template <typename Value, typename Error>
void consumer<Value, Error>::put_error(Error &&error) const {
inline void consumer<Value, Error>::put_error(Error &&error) const {
if (_instance) {
std::exchange(_instance, nullptr)->put_error(std::move(error));
std::exchange(_instance, nullptr)->put_error(
std::move(error));
}
}
template <typename Value, typename Error>
void consumer<Value, Error>::put_error_copy(const Error &error) const {
inline void consumer<Value, Error>::put_error_copy(
const Error &error) const {
if (_instance) {
std::exchange(_instance, nullptr)->put_error_copy(error);
}
}
template <typename Value, typename Error>
void consumer<Value, Error>::put_done() const {
inline void consumer<Value, Error>::put_done() const {
if (_instance) {
std::exchange(_instance, nullptr)->put_done();
}
}
template <typename Value, typename Error>
void consumer<Value, Error>::add_lifetime(lifetime &&lifetime) const {
inline void consumer<Value, Error>::add_lifetime(lifetime &&lifetime) const {
if (_instance) {
_instance->add_lifetime(std::move(lifetime));
} else {
@ -276,20 +264,20 @@ void consumer<Value, Error>::add_lifetime(lifetime &&lifetime) const {
template <typename Value, typename Error>
template <typename Type, typename... Args>
Type *consumer<Value, Error>::make_state(Args&& ...args) const {
inline Type *consumer<Value, Error>::make_state(Args&& ...args) const {
Expects(_instance != nullptr);
return _instance->template make_state<Type>(std::forward<Args>(args)...);
}
template <typename Value, typename Error>
void consumer<Value, Error>::terminate() const {
inline void consumer<Value, Error>::terminate() const {
if (_instance) {
std::exchange(_instance, nullptr)->terminate();
}
}
template <typename Value, typename Error>
void consumer<Value, Error>::abstract_consumer_instance::add_lifetime(
inline void consumer<Value, Error>::abstract_instance::add_lifetime(
lifetime &&lifetime) {
std::unique_lock<std::mutex> lock(_mutex);
if (_terminated) {
@ -303,15 +291,15 @@ void consumer<Value, Error>::abstract_consumer_instance::add_lifetime(
template <typename Value, typename Error>
template <typename Type, typename... Args>
Type *consumer<Value, Error>::abstract_consumer_instance::make_state(
inline Type *consumer<Value, Error>::abstract_instance::make_state(
Args&& ...args) {
std::unique_lock<std::mutex> lock(_mutex);
Expects(!_terminated);
return _lifetime.template make_state<Type>(std::forward<Args>(args)...);
return _lifetime.make_state<Type>(std::forward<Args>(args)...);
}
template <typename Value, typename Error>
void consumer<Value, Error>::abstract_consumer_instance::terminate() {
inline void consumer<Value, Error>::abstract_instance::terminate() {
std::unique_lock<std::mutex> lock(_mutex);
if (!_terminated) {
_terminated = true;
@ -324,7 +312,7 @@ void consumer<Value, Error>::abstract_consumer_instance::terminate() {
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
bool consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_next(
bool consumer<Value, Error>::instance<OnNext, OnError, OnDone>::put_next(
Value &&value) {
std::unique_lock<std::mutex> lock(this->_mutex);
if (this->_terminated) {
@ -333,13 +321,16 @@ bool consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_nex
auto handler = this->_next;
lock.unlock();
handler(std::move(value));
details::callable_helper(
handler,
std::move(value),
details::is_callable_plain<OnNext, Value>());
return true;
}
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
bool consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_next_copy(
bool consumer<Value, Error>::instance<OnNext, OnError, OnDone>::put_next_copy(
const Value &value) {
std::unique_lock<std::mutex> lock(this->_mutex);
if (this->_terminated) {
@ -348,41 +339,47 @@ bool consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_nex
auto handler = this->_next;
lock.unlock();
details::const_ref_call_helper(handler, value, 0);
details::const_ref_call_helper(
handler,
value,
details::allows_const_ref<OnNext, Value>());
return true;
}
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
void consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_error(
void consumer<Value, Error>::instance<OnNext, OnError, OnDone>::put_error(
Error &&error) {
std::unique_lock<std::mutex> lock(this->_mutex);
if (!this->_terminated) {
auto handler = std::move(this->_error);
lock.unlock();
handler(std::move(error));
details::callable_invoke(handler, std::move(error));
this->terminate();
}
}
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
void consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_error_copy(
void consumer<Value, Error>::instance<OnNext, OnError, OnDone>::put_error_copy(
const Error &error) {
std::unique_lock<std::mutex> lock(this->_mutex);
if (!this->_terminated) {
auto handler = std::move(this->_error);
lock.unlock();
details::const_ref_call_helper(handler, error, 0);
details::const_ref_call_helper(
handler,
error,
details::allows_const_ref<OnError, Error>());
this->terminate();
}
}
template <typename Value, typename Error>
template <typename OnNext, typename OnError, typename OnDone>
void consumer<Value, Error>::consumer_instance<OnNext, OnError, OnDone>::put_done() {
void consumer<Value, Error>::instance<OnNext, OnError, OnDone>::put_done() {
std::unique_lock<std::mutex> lock(this->_mutex);
if (!this->_terminated) {
auto handler = std::move(this->_done);

View File

@ -28,7 +28,7 @@ template <
typename Creator,
typename Value = typename decltype(std::declval<Creator>()())::value_type,
typename Error = typename decltype(std::declval<Creator>()())::error_type>
producer<Value, Error> deferred(Creator &&creator) {
inline producer<Value, Error> deferred(Creator &&creator) {
return [creator = std::forward<Creator>(creator)](
const consumer<Value, Error> &consumer) mutable {
return std::move(creator)().start_existing(consumer);

View File

@ -0,0 +1,211 @@
/*
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/build_config.h"
// Custom libc++ build used for old OS X versions already has this.
#ifndef OS_MAC_OLD
#if defined COMPILER_CLANG || defined COMPILER_GCC
namespace std {
#ifdef COMPILER_GCC
template <bool Value>
using bool_constant = integral_constant<bool, Value>;
#endif // COMPILER_GCC
template <typename ...Args>
constexpr auto tuple_size_v = std::tuple_size<Args...>::value;
template <typename ...Args>
constexpr auto is_rvalue_reference_v = is_rvalue_reference<Args...>::value;
template <typename ...Args>
constexpr auto is_base_of_v = is_base_of<Args...>::value;
template <typename ...Args>
constexpr auto is_same_v = is_same<Args...>::value;
namespace detail {
template <typename Method, typename Tuple, size_t... I>
constexpr decltype(auto) apply_impl(
Method &&method,
Tuple &&tuple,
index_sequence<I...>) {
return forward<Method>(method)(get<I>(forward<Tuple>(tuple))...);
}
} // namespace detail
template <class Method, class Tuple>
constexpr decltype(auto) apply(Method &&method, Tuple&& tuple) {
return detail::apply_impl(
forward<Method>(method),
forward<Tuple>(tuple),
make_index_sequence<tuple_size_v<decay_t<Tuple>>>{});
}
} // namespace std
#endif // COMPILER_CLANG || COMPILER_GCC
#endif // OS_MAC_OLD
namespace rpl {
namespace details {
template <typename Arg>
const Arg &const_ref_val() noexcept;
template <typename Arg>
Arg &lvalue_ref_val() noexcept;
using false_t = char;
struct true_t {
false_t data[2];
};
static_assert(sizeof(false_t) != sizeof(true_t), "I can't work :(");
template <
typename Method,
typename ...Args,
typename = decltype(std::declval<Method>()(
std::declval<Args>()...))>
true_t test_callable_plain(Method &&, Args &&...) noexcept;
false_t test_callable_plain(...) noexcept;
template <typename Method, typename ...Args>
struct is_callable_plain
: std::bool_constant<(
sizeof(test_callable_plain(
std::declval<Method>(),
std::declval<Args>()...
)) == sizeof(true_t))> {
};
template <typename Method, typename ...Args>
constexpr bool is_callable_plain_v = is_callable_plain<Method, Args...>::value;
template <
typename Method,
typename ...Types,
typename = decltype(std::declval<Method>()(
std::declval<Types>()...))>
true_t test_callable_tuple(
Method &&,
std::tuple<Types...> &&) noexcept;
false_t test_callable_tuple(...) noexcept;
template <typename Method, typename Arg>
constexpr bool is_callable_tuple_v = (sizeof(test_callable_tuple(
std::declval<Method>(),
std::declval<Arg>())) == sizeof(true_t));
template <typename Method, typename Arg>
struct is_callable_tuple
: std::bool_constant<
is_callable_tuple_v<Method, Arg>> {
};
template <typename Method, typename ...Args>
struct is_callable;
template <typename Method>
struct is_callable<Method>
: std::bool_constant<
is_callable_plain_v<Method>> {
};
template <typename Method, typename Arg>
struct is_callable<Method, Arg>
: std::bool_constant<
is_callable_plain_v<Method, Arg>
|| is_callable_tuple_v<Method, Arg>> {
};
template <typename Method, typename ...Args>
constexpr bool is_callable_v = is_callable<Method, Args...>::value;
template <typename Method, typename Arg>
inline decltype(auto) callable_helper(Method &&method, Arg &&arg, std::true_type) {
return std::move(method)(std::forward<Arg>(arg));
}
template <typename Method, typename Arg>
inline decltype(auto) callable_helper(Method &&method, Arg &&arg, std::false_type) {
return std::apply(std::move(method), std::forward<Arg>(arg));
}
template <typename Method, typename Arg>
inline decltype(auto) callable_invoke(Method &&method, Arg &&arg) {
return callable_helper(
std::forward<Method>(method),
std::forward<Arg>(arg),
is_callable_plain<Method, Arg>());
}
template <typename Method, typename Arg>
using callable_result = decltype(callable_invoke(
std::declval<Method>(),
std::declval<Arg>()));
template <
typename Method,
typename Arg,
typename = decltype(std::declval<Method>()(
const_ref_val<std::decay_t<Arg>>()))>
true_t test_allows_const_ref(Method &&, Arg &&) noexcept;
false_t test_allows_const_ref(...) noexcept;
template <typename Method, typename Arg>
constexpr bool allows_const_ref_v = (sizeof(test_allows_const_ref(
std::declval<Method>(),
std::declval<Arg>())) == sizeof(true_t));
template <typename Method, typename Arg>
struct allows_const_ref
: std::bool_constant<
allows_const_ref_v<Method, Arg>> {
};
template <typename Method, typename Arg>
inline decltype(auto) const_ref_call_helper(
Method &method,
const Arg &arg,
std::true_type) {
return callable_invoke(method, arg);
}
template <typename Method, typename Arg>
inline decltype(auto) const_ref_call_helper(
Method &method,
const Arg &arg,
std::false_type) {
auto copy = arg;
return callable_invoke(method, std::move(copy));
}
} // namespace details
} // namespace rpl

View File

@ -0,0 +1,209 @@
/*
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 <type_traits>
#include "base/variant.h"
namespace rpl {
namespace details {
namespace type_list {
template <typename ...Types>
struct list {
};
template <typename Type, typename ...Types>
struct list<Type, Types...> {
using head = Type;
using tail = list<Types...>;
};
using empty_list = list<>;
template <typename TypeList>
using head_t = typename TypeList::head;
template <typename TypeList>
using tail_t = typename TypeList::tail;
template <typename Head, typename Tail>
struct construct;
template <typename Head, typename Tail>
using construct_t = typename construct<Head, Tail>::type;
template <typename Head, typename ...Types>
struct construct<Head, list<Types...>> {
using type = list<Head, Types...>;
};
template <typename TypeList>
struct size;
template <typename TypeList>
constexpr std::size_t size_v = size<TypeList>::value;
template <typename ...Types>
struct size<list<Types...>>
: std::integral_constant<
std::size_t,
sizeof...(Types)> {
};
template <typename TypeList>
constexpr bool empty_v = (size_v<TypeList> == 0);
template <typename TypeList>
struct empty : std::bool_constant<empty_v<TypeList>> {
};
template <std::size_t Index, typename TypeList>
struct get;
template <std::size_t Index, typename TypeList>
using get_t = typename get<Index, TypeList>::type;
template <std::size_t Index, typename TypeList>
struct get {
using type = get_t<Index - 1, tail_t<TypeList>>;
};
template <typename TypeList>
struct get<0, TypeList> {
using type = head_t<TypeList>;
};
template <typename TypeList1, typename TypeList2>
struct concat;
template <typename TypeList1, typename TypeList2>
using concat_t = typename concat<TypeList1, TypeList2>::type;
template <typename ...Types1, typename ...Types2>
struct concat<list<Types1...>, list<Types2...>> {
using type = list<Types1..., Types2...>;
};
template <typename TypeList, typename Type>
struct remove_all;
template <typename TypeList, typename Type>
using remove_all_t = typename remove_all<TypeList, Type>::type;
template <typename TypeList, typename Type>
struct remove_all {
using head = head_t<TypeList>;
using tail = tail_t<TypeList>;
using clean_tail = remove_all_t<tail, Type>;
using type = std::conditional_t<
std::is_same_v<head, Type>,
clean_tail,
construct_t<head, clean_tail>>;
};
template <typename Type>
struct remove_all<empty_list, Type> {
using type = empty_list;
};
template <typename TypeList>
struct last;
template <typename TypeList>
using last_t = typename last<TypeList>::type;
template <typename TypeList>
struct last {
using type = last_t<tail_t<TypeList>>;
};
template <typename Type>
struct last<list<Type>> {
using type = Type;
};
template <typename TypeList>
struct chop_last;
template <typename TypeList>
using chop_last_t = typename chop_last<TypeList>::type;
template <typename TypeList>
struct chop_last {
using type = construct_t<
head_t<TypeList>,
chop_last_t<tail_t<TypeList>>>;
};
template <typename Type>
struct chop_last<list<Type>> {
using type = empty_list;
};
template <typename TypeList>
struct distinct;
template <typename TypeList>
using distinct_t = typename distinct<TypeList>::type;
template <typename TypeList>
struct distinct {
using type = construct_t<
head_t<TypeList>,
distinct_t<
remove_all_t<tail_t<TypeList>, head_t<TypeList>>>>;
};
template <>
struct distinct<empty_list> {
using type = empty_list;
};
template <typename TypeList, template <typename ...> typename To>
struct extract_to;
template <typename TypeList, template <typename ...> typename To>
using extract_to_t = typename extract_to<TypeList, To>::type;
template <typename ...Types, template <typename ...> typename To>
struct extract_to<list<Types...>, To> {
using type = To<Types...>;
};
} // namespace type_list
template <typename ...Types>
struct normalized_variant {
using list = type_list::list<Types...>;
using distinct = type_list::distinct_t<list>;
using type = std::conditional_t<
type_list::size_v<distinct> == 1,
type_list::get_t<0, distinct>,
type_list::extract_to_t<distinct, base::variant>>;
};
template <typename ...Types>
using normalized_variant_t
= typename normalized_variant<Types...>::type;
} // namespace details
} // namespace rpl

View File

@ -33,7 +33,7 @@ public:
rpl::producer<Value, Error> &&initial) const {
return [initial = std::move(initial)](
const consumer<Value, Error> &consumer) mutable {
auto previous = consumer.make_state<
auto previous = consumer.template make_state<
base::optional<Value>
>();
return std::move(initial).start(

View File

@ -65,17 +65,17 @@ private:
};
template <typename Value>
event_stream<Value>::event_stream() {
inline event_stream<Value>::event_stream() {
}
template <typename Value>
event_stream<Value>::event_stream(event_stream &&other)
: _consumers(base::take(other._consumers)) {
inline event_stream<Value>::event_stream(event_stream &&other)
: _consumers(base::take(other._consumers)) {
}
template <typename Value>
template <typename OtherValue>
void event_stream<Value>::fire_forward(OtherValue &&value) {
inline void event_stream<Value>::fire_forward(OtherValue &&value) {
if (!_consumers) {
return;
}
@ -116,7 +116,7 @@ void event_stream<Value>::fire_forward(OtherValue &&value) {
}
template <typename Value>
producer<Value, no_error> event_stream<Value>::events() const {
inline producer<Value, no_error> event_stream<Value>::events() const {
return producer<Value, no_error>([weak = weak()](
const consumer<Value, no_error> &consumer) {
if (auto strong = weak.lock()) {
@ -136,7 +136,7 @@ producer<Value, no_error> event_stream<Value>::events() const {
}
template <typename Value>
std::weak_ptr<std::vector<consumer<Value, no_error>>> event_stream<Value>::weak() const {
inline std::weak_ptr<std::vector<consumer<Value, no_error>>> event_stream<Value>::weak() const {
if (!_consumers) {
_consumers = std::make_shared<std::vector<consumer<Value, no_error>>>();
}
@ -145,7 +145,7 @@ std::weak_ptr<std::vector<consumer<Value, no_error>>> event_stream<Value>::weak(
template <typename Value>
event_stream<Value>::~event_stream() {
inline event_stream<Value>::~event_stream() {
if (auto consumers = base::take(_consumers)) {
for (auto &consumer : *consumers) {
consumer.put_done();

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace rpl {
template <typename Value, typename Error>
producer<Value, std::decay_t<Error>> fail(Error &&error) {
inline producer<Value, std::decay_t<Error>> fail(Error &&error) {
using consumer_t = consumer<Value, std::decay_t<Error>>;
return [error = std::forward<Error>(error)](
const consumer_t &consumer) mutable {

View File

@ -66,7 +66,7 @@ private:
} // namespace details
template <typename Predicate>
auto filter(Predicate &&predicate)
inline auto filter(Predicate &&predicate)
-> details::filter_helper<std::decay_t<Predicate>> {
return details::filter_helper<std::decay_t<Predicate>>(
std::forward<Predicate>(predicate));

View File

@ -34,22 +34,24 @@ public:
map_transform_helper(
Transform &&transform,
const consumer<NewValue, Error> &consumer)
: _transform(std::move(transform))
, _consumer(consumer) {
: _consumer(consumer)
, _transform(std::move(transform)) {
}
template <
typename OtherValue,
typename = std::enable_if_t<
std::is_rvalue_reference_v<OtherValue&&>>>
void operator()(OtherValue &&value) const {
_consumer.put_next_forward(_transform(std::move(value)));
_consumer.put_next_forward(
details::callable_invoke(_transform, std::move(value)));
}
template <
typename OtherValue,
typename = decltype(
std::declval<Transform>()(const_ref_val<OtherValue>()))>
void operator()(const OtherValue &value) const {
_consumer.put_next_forward(_transform(value));
_consumer.put_next_forward(
details::callable_invoke(_transform, value));
}
private:
@ -64,7 +66,8 @@ template <
typename Error,
typename = std::enable_if_t<
std::is_rvalue_reference_v<Transform&&>>>
map_transform_helper<Transform, NewValue, Error> map_transform(
inline map_transform_helper<Transform, NewValue, Error>
map_transform(
Transform &&transform,
const consumer<NewValue, Error> &consumer) {
return { std::move(transform), consumer };
@ -81,9 +84,9 @@ public:
template <
typename Value,
typename Error,
typename NewValue = decltype(
std::declval<Transform>()(std::declval<Value>())
)>
typename NewValue = details::callable_result<
Transform,
Value>>
rpl::producer<NewValue, Error> operator()(
rpl::producer<Value, Error> &&initial) {
return [
@ -111,7 +114,7 @@ private:
} // namespace details
template <typename Transform>
auto map(Transform &&transform)
inline auto map(Transform &&transform)
-> details::map_helper<std::decay_t<Transform>> {
return details::map_helper<std::decay_t<Transform>>(
std::forward<Transform>(transform));
@ -136,14 +139,16 @@ public:
typename = std::enable_if_t<
std::is_rvalue_reference_v<OtherError&&>>>
void operator()(OtherError &&error) const {
_consumer.put_error_forward(_transform(std::move(error)));
_consumer.put_error_forward(
details::callable_invoke(_transform, std::move(error)));
}
template <
typename OtherError,
typename = decltype(
std::declval<Transform>()(const_ref_val<OtherError>()))>
void operator()(const OtherError &error) const {
_consumer.put_error_forward(_transform(error));
_consumer.put_error_forward(
details::callable_invoke(_transform, error));
}
private:
@ -158,7 +163,7 @@ template <
typename NewError,
typename = std::enable_if_t<
std::is_rvalue_reference_v<Transform&&>>>
map_error_transform_helper<Transform, Value, NewError>
inline map_error_transform_helper<Transform, Value, NewError>
map_error_transform(
Transform &&transform,
const consumer<Value, NewError> &consumer) {
@ -176,9 +181,9 @@ public:
template <
typename Value,
typename Error,
typename NewError = decltype(
std::declval<Transform>()(std::declval<Error>())
)>
typename NewError = details::callable_result<
Transform,
Error>>
rpl::producer<Value, NewError> operator()(
rpl::producer<Value, Error> &&initial) {
return [
@ -206,7 +211,7 @@ private:
} // namespace details
template <typename Transform>
auto map_error(Transform &&transform)
inline auto map_error(Transform &&transform)
-> details::map_error_helper<std::decay_t<Transform>> {
return details::map_error_helper<std::decay_t<Transform>>(
std::forward<Transform>(transform));

View File

@ -0,0 +1,466 @@
/*
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
namespace rpl {
namespace details {
struct base_mapper {
};
template <typename Type>
constexpr bool is_mapper_v = std::is_base_of_v<
base_mapper,
std::decay_t<Type>>;
template <std::size_t Index>
struct argument_mapper : base_mapper {
template <
typename Arg,
typename ...Args,
typename = std::enable_if_t<(sizeof...(Args) >= Index)>>
static constexpr decltype(auto) call(Arg &&arg, Args &&...args) {
return argument_mapper<Index - 1>::call(
std::forward<Args>(args)...);
}
template <
typename ...Args,
typename = std::enable_if_t<(sizeof...(Args) > Index)>>
constexpr auto operator()(Args &&...args) const {
return call(std::forward<Args>(args)...);
}
};
template <>
struct argument_mapper<0> : base_mapper {
template <
typename Arg,
typename ...Args>
static constexpr decltype(auto) call(Arg &&arg, Args &&...args) {
return std::forward<Arg>(arg);
}
template <
typename Arg,
typename ...Args>
constexpr auto operator()(Arg &&arg, Args &&...args) const {
return std::forward<Arg>(arg);
}
};
template <typename Type>
class value_mapper : public base_mapper {
public:
template <typename OtherType>
constexpr value_mapper(OtherType &&value)
: _value(std::forward<OtherType>(value)) {
}
template <typename ...Args>
constexpr auto operator()(Args &&...args) const {
return _value;
}
private:
Type _value;
};
template <typename Type>
inline value_mapper<std::decay_t<Type>> make_value_mapper(Type &&value) {
return { std::forward<Type>(value) };
}
template <typename Type>
struct wrap_mapper {
using type = std::conditional_t<
is_mapper_v<Type>,
Type,
value_mapper<Type>>;
};
template <typename Type>
using wrap_mapper_t = typename wrap_mapper<Type>::type;
template <typename Type, typename Operator>
class unary_operator_mapper : public base_mapper {
using TypeWrapper = wrap_mapper_t<std::decay_t<Type>>;
public:
template <typename OtherType>
constexpr unary_operator_mapper(OtherType &&value)
: _value(std::forward<OtherType>(value)) {
}
template <
typename ...Args,
typename Result = decltype((Operator{})(
std::declval<TypeWrapper>()(std::declval<Args>()...)))>
constexpr std::decay_t<Result> operator()(Args &&...args) const {
return (Operator{})(
_value(std::forward<Args>(args)...));
}
private:
TypeWrapper _value;
};
template <typename Left, typename Right, typename Operator>
class binary_operator_mapper : public base_mapper {
using LeftWrapper = wrap_mapper_t<std::decay_t<Left>>;
using RightWrapper = wrap_mapper_t<std::decay_t<Right>>;
public:
template <typename OtherLeft, typename OtherRight>
constexpr binary_operator_mapper(OtherLeft &&left, OtherRight &&right)
: _left(std::forward<OtherLeft>(left))
, _right(std::forward<OtherRight>(right)) {
}
template <
typename ...Args,
typename Result = decltype((Operator{})(
std::declval<LeftWrapper>()(std::declval<Args>()...),
std::declval<RightWrapper>()(std::declval<Args>()...)))>
constexpr std::decay_t<Result> operator()(Args &&...args) const {
return (Operator{})(
_left(std::forward<Args>(args)...),
_right(std::forward<Args>(args)...));
}
private:
LeftWrapper _left;
RightWrapper _right;
};
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator+(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::plus<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator-(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::minus<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator*(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::multiplies<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator/(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::divides<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator%(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::modulus<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Type,
typename = std::enable_if_t<
is_mapper_v<Type>
>>
inline auto operator-(Type &&value) {
return unary_operator_mapper<
Type,
std::negate<>>(
std::forward<Type>(value));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator<(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::less<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator<=(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::less_equal<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator>(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::greater<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator>=(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::greater_equal<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator==(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::equal_to<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator!=(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::not_equal_to<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator&&(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::logical_and<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator||(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::logical_or<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Type,
typename = std::enable_if_t<
is_mapper_v<Type>
>>
inline auto operator!(Type &&value) {
return unary_operator_mapper<
Type,
std::logical_not<>>(
std::forward<Type>(value));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator&(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::bit_and<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator|(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::bit_or<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Left,
typename Right,
typename = std::enable_if_t<
is_mapper_v<Left> || is_mapper_v<Right>
>>
inline auto operator^(Left &&left, Right &&right) {
return binary_operator_mapper<
Left,
Right,
std::bit_xor<>>(
std::forward<Left>(left),
std::forward<Right>(right));
}
template <
typename Type,
typename = std::enable_if_t<
is_mapper_v<Type>
>>
inline auto operator~(Type &&value) {
return unary_operator_mapper<
Type,
std::bit_not<>>(
std::forward<Type>(value));
}
} // namespace details
namespace mappers {
constexpr const details::argument_mapper<0> $1;
constexpr const details::argument_mapper<1> $2;
constexpr const details::argument_mapper<2> $3;
constexpr const details::argument_mapper<3> $4;
constexpr const details::argument_mapper<4> $5;
constexpr const details::argument_mapper<5> $6;
constexpr const details::argument_mapper<6> $7;
constexpr const details::argument_mapper<7> $8;
constexpr const details::argument_mapper<8> $9;
constexpr const details::argument_mapper<9> $10;
constexpr const details::argument_mapper<10> $11;
constexpr const details::argument_mapper<11> $12;
constexpr const details::argument_mapper<12> $13;
constexpr const details::argument_mapper<13> $14;
constexpr const details::argument_mapper<14> $15;
constexpr const details::argument_mapper<15> $16;
constexpr const details::argument_mapper<16> $17;
constexpr const details::argument_mapper<17> $18;
constexpr const details::argument_mapper<18> $19;
constexpr const details::argument_mapper<19> $20;
template <typename Type>
inline auto $val(Type &&value) {
return details::make_value_mapper(std::forward<Type>(value));
}
} // namespace mappers
} // namespace rpl

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace rpl {
template <typename Value = empty_value, typename Error = no_error>
producer<Value, Error> never() {
inline producer<Value, Error> never() {
return [](const consumer<Value, Error> &consumer) mutable {
return lifetime();
};

View File

@ -69,6 +69,7 @@ public:
if (_copyCounter) {
++*_copyCounter;
}
return *this;
}
InvokeCounter &operator=(InvokeCounter &&other) {
_copyCounter = base::take(other._copyCounter);
@ -76,6 +77,7 @@ public:
if (_moveCounter) {
++*_moveCounter;
}
return *this;
}
private:
@ -133,16 +135,19 @@ TEST_CASE("basic operators tests", "[rpl::operators]") {
rpl::lifetime lifetime;
std::move(testing)
| then(complete<InvokeCounter>())
| on_next([=](InvokeCounter&&) {
(void)destroyCalled;
++*sum;
}) | on_error([=](no_error) {
(void)destroyCalled;
}) | on_done([=] {
(void)destroyCalled;
*doneGenerated = true;
}) | start(lifetime);
| then(complete<InvokeCounter>())
| on_next([=](InvokeCounter&&) {
(void)destroyCalled;
++*sum;
})
| on_error([=](no_error) {
(void)destroyCalled;
})
| on_done([=] {
(void)destroyCalled;
*doneGenerated = true;
})
| start(lifetime);
}
REQUIRE(*sum == 5);
REQUIRE(*doneGenerated);
@ -160,10 +165,12 @@ TEST_CASE("basic operators tests", "[rpl::operators]") {
| then(single(4))
| then(single(5))
| map([](int value) {
return std::to_string(value);
}) | on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
}) | start(lifetime);
return std::to_string(value);
})
| on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
})
| start(lifetime);
}
REQUIRE(*sum == "1 2 3 4 5 ");
}
@ -186,7 +193,8 @@ TEST_CASE("basic operators tests", "[rpl::operators]") {
| on_next([=](int value) {
REQUIRE(++*checked == *launched);
REQUIRE(*checked == value);
}) | start(lifetime);
})
| start(lifetime);
REQUIRE(*launched == 5);
}
}
@ -203,9 +211,11 @@ TEST_CASE("basic operators tests", "[rpl::operators]") {
| filter([](int value) { return value != 2; })
| map([](int value) {
return std::to_string(value);
}) | on_next([=](std::string &&value) {
})
| on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
}) | start(lifetime);
})
| start(lifetime);
}
REQUIRE(*sum == "1 1 3 ");
}
@ -221,10 +231,12 @@ TEST_CASE("basic operators tests", "[rpl::operators]") {
| then(single(3))
| distinct_until_changed()
| map([](int value) {
return std::to_string(value);
}) | on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
}) | start(lifetime);
return std::to_string(value);
})
| on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
})
| start(lifetime);
}
REQUIRE(*sum == "1 2 3 ");
}
@ -238,11 +250,130 @@ TEST_CASE("basic operators tests", "[rpl::operators]") {
| then(single(single(5) | then(single(6))))
| flatten_latest()
| map([](int value) {
return std::to_string(value);
}) | on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
}) | start(lifetime);
return std::to_string(value);
})
| on_next([=](std::string &&value) {
*sum += std::move(value) + ' ';
})
| start(lifetime);
}
REQUIRE(*sum == "1 2 3 4 5 6 ");
}
SECTION("combine vector test") {
auto sum = std::make_shared<std::string>("");
{
rpl::lifetime lifetime;
rpl::event_stream<bool> a;
rpl::event_stream<bool> b;
rpl::event_stream<bool> c;
std::vector<rpl::producer<bool>> v;
v.push_back(a.events());
v.push_back(b.events());
v.push_back(c.events());
combine(std::move(v), [](const auto &values) {
return values[0] && values[1] && !values[2];
})
| on_next([=](bool value) {
*sum += std::to_string(value ? 1 : 0);
})
| start(lifetime);
a.fire(true);
b.fire(true);
c.fire(false);
a.fire(false);
b.fire(true);
a.fire(true);
c.fire(true);
}
REQUIRE(*sum == "10010");
}
SECTION("combine test") {
auto sum = std::make_shared<std::string>("");
{
rpl::lifetime lifetime;
rpl::event_stream<int> a;
rpl::event_stream<short> b;
rpl::event_stream<char> c;
combine(
a.events(),
b.events(),
c.events(),
[](long a, long b, long c) {
return a;
})
| on_next([=](int value) {
*sum += std::to_string(value);
})
| start(lifetime);
combine(
a.events(),
b.events(),
c.events(),
[](auto &&value) {
return std::get<1>(value);
})
| on_next([=](int value) {
*sum += std::to_string(value);
})
| start(lifetime);
combine(a.events(), b.events(), c.events())
| map([](auto &&value) {
return std::make_tuple(
std::to_string(std::get<0>(value)),
std::to_string(std::get<1>(value)),
std::to_string(std::get<2>(value)));
})
| on_next([=](auto &&value) {
*sum += std::get<0>(value) + ' '
+ std::get<1>(value) + ' '
+ std::get<2>(value) + ' ';
})
| start(lifetime);
a.fire(1);
b.fire(2);
c.fire(3);
a.fire(4);
b.fire(5);
c.fire(6);
}
REQUIRE(*sum == "121 2 3 424 2 3 454 5 3 454 5 6 ");
}
SECTION("mappers test") {
auto sum = std::make_shared<std::string>("");
{
rpl::lifetime lifetime;
rpl::event_stream<int> a;
rpl::event_stream<short> b;
rpl::event_stream<char> c;
using namespace mappers;
combine(
a.events(),
b.events(),
c.events(),
$1 + $2 + $3 + 10)
| on_next([=](int value) {
*sum += std::to_string(value);
})
| start(lifetime);
a.fire(1);
b.fire(2);
c.fire(3);
a.fire(4);
b.fire(5);
c.fire(6);
}
REQUIRE(*sum == "16192225");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -157,6 +157,21 @@ TEST_CASE("basic producer tests", "[rpl::producer]") {
saved.destroy();
REQUIRE(*lifetimeEndCount == 3);
}
SECTION("tuple producer test") {
auto result = std::make_shared<int>(0);
{
producer<std::tuple<int, double>>([=](auto &&consumer) {
consumer.put_next(std::make_tuple(1, 2.));
return lifetime();
}).start([=](int a, double b) {
*result = a + int(b);
}, [=](no_error error) {
}, [=]() {
});
}
REQUIRE(*result == 3);
}
}
TEST_CASE("basic event_streams tests", "[rpl::event_stream]") {
@ -447,4 +462,35 @@ TEST_CASE("basic piping tests", "[rpl::producer]") {
}
REQUIRE(*sum == 3);
}
SECTION("rich start calls") {
auto sum = std::make_shared<int>(0);
{
auto alive = lifetime();
producer<int, int>([=](auto &&consumer) {
consumer.put_next(33);
return lifetime();
})
| start([=](int value) {
*sum += value;
}, alive);
producer<no_value, int>([=](auto &&consumer) {
consumer.put_error(33);
return lifetime();
})
| start([](no_value) {
}, [=](int value) {
*sum += value;
}, alive);
producer<int, int>([=](auto &&consumer) {
consumer.put_next(33);
consumer.put_done();
return lifetime();
})
| start([=] {
*sum += 33;
}, alive);
}
REQUIRE(*sum == 99);
}
}

View File

@ -33,9 +33,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/then.h>
#include <rpl/deferred.h>
#include <rpl/map.h>
#include <rpl/mappers.h>
#include <rpl/filter.h>
#include <rpl/distinct_until_changed.h>
#include <rpl/flatten_latest.h>
#include <rpl/combine_latest.h>
#include <rpl/combine.h>
#include <rpl/before_next.h>

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace rpl {
template <typename Value, typename Error = no_error>
producer<std::decay_t<Value>, Error> single(Value &&value) {
inline producer<std::decay_t<Value>, Error> single(Value &&value) {
using consumer_t = consumer<std::decay_t<Value>, Error>;
return [value = std::forward<Value>(value)](
const consumer_t &consumer) mutable {
@ -36,7 +36,7 @@ producer<std::decay_t<Value>, Error> single(Value &&value) {
}
template <typename Error = no_error>
producer<empty_value, Error> single() {
inline producer<empty_value, Error> single() {
return [](const consumer<empty_value, Error> &consumer) {
consumer.put_next({});
consumer.put_done();

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace rpl {
template <typename Value, typename Error>
auto then(producer<Value, Error> &&following) {
inline auto then(producer<Value, Error> &&following) {
return [following = std::move(following)](
producer<Value, Error> &&initial) mutable
-> producer<Value, Error> {

View File

@ -97,10 +97,11 @@
'common_test.gypi',
],
'sources': [
'<(src_loc)/rpl/details/callable.h',
'<(src_loc)/rpl/details/type_list.h',
'<(src_loc)/rpl/before_next.h',
'<(src_loc)/rpl/combine_latest.h',
'<(src_loc)/rpl/combine.h',
'<(src_loc)/rpl/complete.h',
'<(src_loc)/rpl/combine_latest.h',
'<(src_loc)/rpl/consumer.h',
'<(src_loc)/rpl/deferred.h',
'<(src_loc)/rpl/distinct_until_changed.h',
@ -110,6 +111,7 @@
'<(src_loc)/rpl/flatten_latest.h',
'<(src_loc)/rpl/lifetime.h',
'<(src_loc)/rpl/map.h',
'<(src_loc)/rpl/mappers.h',
'<(src_loc)/rpl/never.h',
'<(src_loc)/rpl/operators_tests.cpp',
'<(src_loc)/rpl/producer.h',