// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #pragma once #include #include 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)); }; 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)); } variable(const variable &other) : _data(other._data) { } variable &operator=(const variable &other) { return (*this = 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 = std::enable_if_t< std::is_assignable_v>> void force_assign(OtherType &&data) { _lifetime.destroy(); _data = std::forward(data); _changes.fire_copy(_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; } std::conditional_t< (std::is_trivially_copyable_v && sizeof(Type) <= 16), Type, const 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