variable.h 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include <rpl/producer.h>
  9. #include <rpl/event_stream.h>
  10. namespace rpl {
  11. namespace details {
  12. template <typename A, typename B>
  13. struct supports_equality_compare {
  14. template <typename U, typename V>
  15. static auto test(const U *u, const V *v)
  16. -> decltype(*u == *v, true_t());
  17. static false_t test(...);
  18. static constexpr bool value
  19. = (sizeof(test((const A*)nullptr, (const B*)nullptr))
  20. == sizeof(true_t));
  21. };
  22. template <typename A, typename B>
  23. constexpr bool supports_equality_compare_v
  24. = supports_equality_compare<std::decay_t<A>, std::decay_t<B>>::value;
  25. } // namespace details
  26. template <typename Type, typename Error = no_error>
  27. class variable final {
  28. public:
  29. variable() : _data{} {
  30. }
  31. variable(variable &&other) : _data(std::move(other._data)) {
  32. }
  33. variable &operator=(variable &&other) {
  34. return (*this = std::move(other._data));
  35. }
  36. variable(const variable &other) : _data(other._data) {
  37. }
  38. variable &operator=(const variable &other) {
  39. return (*this = other._data);
  40. }
  41. template <
  42. typename OtherType,
  43. typename = std::enable_if_t<
  44. std::is_constructible_v<Type, OtherType&&>>>
  45. variable(OtherType &&data) : _data(std::forward<OtherType>(data)) {
  46. }
  47. template <
  48. typename OtherType,
  49. typename = std::enable_if_t<
  50. std::is_assignable_v<Type&, OtherType&&>>>
  51. variable &operator=(OtherType &&data) {
  52. _lifetime.destroy();
  53. return assign(std::forward<OtherType>(data));
  54. }
  55. template <
  56. typename OtherType,
  57. typename = std::enable_if_t<
  58. std::is_assignable_v<Type&, OtherType&&>>>
  59. void force_assign(OtherType &&data) {
  60. _lifetime.destroy();
  61. _data = std::forward<OtherType>(data);
  62. _changes.fire_copy(_data);
  63. }
  64. template <
  65. typename OtherType,
  66. typename OtherError,
  67. typename Generator,
  68. typename = std::enable_if_t<
  69. std::is_assignable_v<Type&, OtherType>>>
  70. variable(producer<OtherType, OtherError, Generator> &&stream) {
  71. std::move(stream)
  72. | start_with_next([=](auto &&data) {
  73. assign(std::forward<decltype(data)>(data));
  74. }, _lifetime);
  75. }
  76. template <
  77. typename OtherType,
  78. typename OtherError,
  79. typename Generator,
  80. typename = std::enable_if_t<
  81. std::is_assignable_v<Type&, OtherType>>>
  82. variable &operator=(
  83. producer<OtherType, OtherError, Generator> &&stream) {
  84. _lifetime.destroy();
  85. std::move(stream)
  86. | start_with_next([=](auto &&data) {
  87. assign(std::forward<decltype(data)>(data));
  88. }, _lifetime);
  89. return *this;
  90. }
  91. std::conditional_t<
  92. (std::is_trivially_copyable_v<Type> && sizeof(Type) <= 16),
  93. Type,
  94. const Type &> current() const {
  95. return _data;
  96. }
  97. auto value() const {
  98. return _changes.events_starting_with_copy(_data);
  99. }
  100. auto changes() const {
  101. return _changes.events();
  102. }
  103. // Send 'done' to all subscribers and unsubscribe them.
  104. template <
  105. typename OtherType,
  106. typename = std::enable_if_t<
  107. std::is_assignable_v<Type&, OtherType>>>
  108. void reset(OtherType &&data) {
  109. _data = std::forward<OtherType>(data);
  110. _changes = event_stream<Type, Error>();
  111. }
  112. void reset() {
  113. reset(Type());
  114. }
  115. template <
  116. typename OtherError,
  117. typename = std::enable_if_t<
  118. std::is_constructible_v<Error, OtherError&&>>>
  119. void reset_with_error(OtherError &&error) {
  120. _changes.fire_error(std::forward<OtherError>(error));
  121. }
  122. void reset_with_error() {
  123. reset_with_error(Error());
  124. }
  125. private:
  126. template <typename OtherType>
  127. variable &assign(OtherType &&data) {
  128. if constexpr (details::supports_equality_compare_v<Type, OtherType>) {
  129. if (!(_data == data)) {
  130. _data = std::forward<OtherType>(data);
  131. _changes.fire_copy(_data);
  132. }
  133. } else if constexpr (details::supports_equality_compare_v<Type, Type>) {
  134. auto old = std::move(_data);
  135. _data = std::forward<OtherType>(data);
  136. if (!(_data == old)) {
  137. _changes.fire_copy(_data);
  138. }
  139. } else {
  140. _data = std::forward<OtherType>(data);
  141. _changes.fire_copy(_data);
  142. }
  143. return *this;
  144. }
  145. Type _data{};
  146. event_stream<Type, Error> _changes;
  147. lifetime _lifetime;
  148. };
  149. } // namespace rpl