/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include #include namespace mapbox { namespace util { template class variant; } // namespace util } // namespace mapbox namespace rpl { namespace details { template struct supports_equality_compare { template static auto test(const U *u, const V *v) -> decltype(*u == *v, true_t()); static false_t test(...); static constexpr bool value = (sizeof(test((const A*)nullptr, (const B*)nullptr)) == sizeof(true_t)); }; // Fix for MSVC expression SFINAE. // It still doesn't work! :( // //template //struct supports_equality_compare< // mapbox::util::variant, // mapbox::util::variant> { // static constexpr bool value // = (supports_equality_compare::value // && supports_equality_compare< // mapbox::util::variant, // mapbox::util::variant>::value); // //}; //template //struct supports_equality_compare< // mapbox::util::variant, // mapbox::util::variant> { // static constexpr bool value = supports_equality_compare::value; // //}; template constexpr bool supports_equality_compare_v = supports_equality_compare, std::decay_t>::value; } // namespace details template class variable final { public: variable() : _data{} { } variable(variable &&other) : _data(std::move(other._data)) { } variable &operator=(variable &&other) { return (*this = std::move(other._data)); } template < typename OtherType, typename = std::enable_if_t< std::is_constructible_v>> variable(OtherType &&data) : _data(std::forward(data)) { } template < typename OtherType, typename = std::enable_if_t< std::is_assignable_v>> variable &operator=(OtherType &&data) { _lifetime.destroy(); return assign(std::forward(data)); } template < typename OtherType, typename OtherError, typename Generator, typename = std::enable_if_t< std::is_assignable_v>> variable(producer &&stream) { std::move(stream) | start_with_next([=](auto &&data) { assign(std::forward(data)); }, _lifetime); } template < typename OtherType, typename OtherError, typename Generator, typename = std::enable_if_t< std::is_assignable_v>> variable &operator=( producer &&stream) { _lifetime.destroy(); std::move(stream) | start_with_next([=](auto &&data) { assign(std::forward(data)); }, _lifetime); return *this; } Type current() const { return _data; } auto value() const { return _changes.events_starting_with_copy(_data); } auto changes() const { return _changes.events(); } // Send 'done' to all subscribers and unsubscribe them. template < typename OtherType, typename = std::enable_if_t< std::is_assignable_v>> void reset(OtherType &&data) { _data = std::forward(data); _changes = event_stream(); } void reset() { reset(Type()); } template < typename OtherError, typename = std::enable_if_t< std::is_constructible_v>> void reset_with_error(OtherError &&error) { _changes.fire_error(std::forward(error)); } void reset_with_error() { reset_with_error(Error()); } private: template variable &assign(OtherType &&data) { if constexpr (details::supports_equality_compare_v) { if (!(_data == data)) { _data = std::forward(data); _changes.fire_copy(_data); } } else if constexpr (details::supports_equality_compare_v) { auto old = std::move(_data); _data = std::forward(data); if (!(_data == old)) { _changes.fire_copy(_data); } } else { _data = std::forward(data); _changes.fire_copy(_data); } return *this; } Type _data; event_stream _changes; lifetime _lifetime; }; } // namespace rpl