diff --git a/CHANGELOG.md b/CHANGELOG.md index ce259f00..94f1810e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,61 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [0.12.0](https://github.com/YarikTH/ureact/releases/tag/0.12.0) (2023-05-09) + +[Full Changelog](https://github.com/YarikTH/ureact/compare/0.11.0...0.12.0) + +Rework "has_changed" extension point and replace doctest with Cache2 + +- BREAKING! rework approach to "calming" made via has_changed customization + point + Overloaded free function version of `ureact::detail::has_changed()` is + replaced with Customization Point Object + Existence of operator== is no longer condition for "calming", instead free + function `has_changed` is used. + `has_changed` is provided only for arithmetic types, enum types and library + classes `ureact::signal` and `ureact::events`. + + Examples of `has_changed` function: + 1. free function in the same namespace + ```C++ + namespace foo + { + struct Foo + { + int value; + }; + + constexpr bool has_changed( const Foo lhs, const Foo rhs ) noexcept + { + return !( lhs.value == rhs.value ); + } + } + ``` + + 2. hidden friend version for the shy ones + ```C++ + namespace bar + { + class Bar + { + int value; + + friend constexpr bool has_changed( const Bar lhs, const Bar rhs ) noexcept + { + return !( lhs.value == rhs.value ); + } + }; + } + ``` +- Remove Codacy badge. Codacy proved to be useless for this repository +- Replace doctest with Cache2. They are mostly the same in terms of interface, + but activity and support of Cache2 is better +- Move the only benchmark from standalone benchmark folder into ureact_test + target. + Cache2 has microbenchmark feature, but I don't understand it's output and + don't trust it. + ## [0.11.0](https://github.com/YarikTH/ureact/releases/tag/0.11.0) (2023-05-01) [Full Changelog](https://github.com/YarikTH/ureact/compare/0.10.1...0.11.0) diff --git a/include/ureact/version.hpp b/include/ureact/version.hpp index 8e23c9fc..8f90a3c0 100644 --- a/include/ureact/version.hpp +++ b/include/ureact/version.hpp @@ -13,7 +13,7 @@ #define UREACT_VERSION_MAJOR 0 #define UREACT_VERSION_MINOR 12 #define UREACT_VERSION_PATCH 0 -#define UREACT_VERSION_STR "0.12.0 wip" +#define UREACT_VERSION_STR "0.12.0" #define UREACT_VERSION \ ( UREACT_VERSION_MAJOR * 10000 + UREACT_VERSION_MINOR * 100 + UREACT_VERSION_PATCH ) diff --git a/single_include/ureact/ureact_amalgamated.hpp b/single_include/ureact/ureact_amalgamated.hpp index 73200179..06c3f33c 100644 --- a/single_include/ureact/ureact_amalgamated.hpp +++ b/single_include/ureact/ureact_amalgamated.hpp @@ -9,8 +9,8 @@ // http://www.boost.org/LICENSE_1_0.txt) // // ---------------------------------------------------------------- -// Ureact v0.12.0 wip -// Generated: 2023-05-01 19:48:00.253156 +// Ureact v0.12.0 +// Generated: 2023-05-09 17:49:17.123193 // ---------------------------------------------------------------- // ureact - C++ header-only FRP library // The library is heavily influenced by cpp.react - https://github.com/snakster/cpp.react @@ -34,7 +34,7 @@ #define UREACT_VERSION_MAJOR 0 #define UREACT_VERSION_MINOR 12 #define UREACT_VERSION_PATCH 0 -#define UREACT_VERSION_STR "0.12.0 wip" +#define UREACT_VERSION_STR "0.12.0" #define UREACT_VERSION \ ( UREACT_VERSION_MAJOR * 10000 + UREACT_VERSION_MINOR * 100 + UREACT_VERSION_PATCH ) @@ -2552,6 +2552,16 @@ class events : protected detail::events_internals template friend Ret detail::create_wrapped_node( Args&&... args ); + +private: + /*! + * @brief has_changed overload for @ref events + */ + UREACT_WARN_UNUSED_RESULT friend constexpr bool has_changed( + const events& lhs, const events& rhs ) noexcept + { + return !lhs.equal_to( rhs ); + } }; /*! @@ -3238,82 +3248,72 @@ UREACT_END_NAMESPACE UREACT_BEGIN_NAMESPACE -template -class signal; +#if defined( __clang__ ) && defined( __clang_minor__ ) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#endif -template -class events; +/*! + * @brief has_changed overload for arithmetic types and enums + * + * @note Can't use ADL, so should be placed before has_changed_comparable + * @note float-equal warning is suppressed, because it is perfectly fine to compare them in this context + */ +template || std::is_enum_v>> +UREACT_WARN_UNUSED_RESULT constexpr bool has_changed( const T lhs, const T rhs ) noexcept +{ + return !( lhs == rhs ); +} -namespace detail +#if defined( __clang__ ) && defined( __clang_minor__ ) +# pragma clang diagnostic pop +#endif + +// msvc finds ureact::detail::has_changed niebloid if it is not in the different namespace +// as a result infinite recursion occurs. +// to prevent this additional detail namespace is provided. +namespace has_changed_detail { -/// c++17 analog of equality_comparable concept from c++20 -/// https://en.cppreference.com/w/cpp/concepts/equality_comparable template -struct equality_comparable : std::false_type +struct has_changed_comparable : std::false_type {}; -// TODO: check if result of == is exactly bool template -struct equality_comparable() == std::declval() )>> - : std::true_type +struct has_changed_comparable(), std::declval() ) )>> : std::true_type {}; template -inline constexpr bool equality_comparable_v = equality_comparable::value; +inline constexpr bool has_changed_comparable_v = has_changed_comparable::value; -} // namespace detail - -#if defined( __clang__ ) && defined( __clang_minor__ ) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wfloat-equal" -#endif - -/*! - * @brief std::not_equal_to analog intended to prevent reaction of signals to setting the same value as before aka "calming" - * - * Additionally: - * * it equally compares signal and events even if their operator== is overloaded - * * it equally compares reference wrappers because they can be used as S for signal and their operator== does unexpected compare - * * it returns true if types are not equally comparable otherwise - */ -template -UREACT_WARN_UNUSED_RESULT constexpr bool has_changed( const T& lhs, const T& rhs ) +struct HasChangedCPO { - if constexpr( detail::equality_comparable_v ) - { - return !( lhs == rhs ); - } - else + template + constexpr bool operator()( const T& lhs, const T& rhs ) const noexcept { - return true; + if constexpr( has_changed_comparable_v ) + return has_changed( lhs, rhs ); + else + return true; // Assume always changed } -} +}; -// TODO: check if lhs.equal_to( rhs ) can be called instead of checking for specific types -template -UREACT_WARN_UNUSED_RESULT constexpr bool has_changed( const signal& lhs, const signal& rhs ) -{ - return !lhs.equal_to( rhs ); -} +} // namespace has_changed_detail -template -UREACT_WARN_UNUSED_RESULT constexpr bool has_changed( const events& lhs, const events& rhs ) +namespace detail { - return !lhs.equal_to( rhs ); -} -template -UREACT_WARN_UNUSED_RESULT constexpr bool has_changed( // - const std::reference_wrapper& lhs, // - const std::reference_wrapper& rhs ) -{ - return has_changed( lhs.get(), rhs.get() ); -} +/*! + * @brief std::not_equal_to analog intended to prevent reaction of signals to setting the same value as before aka "calming" + * + * @note To make a user-defined type "calmable", provide correct has_changed function + * for the type in the same namespace the type is defined. + * Expected signature is "bool has_changed( const T& lhs, const T& rhs )" + */ +inline constexpr has_changed_detail::HasChangedCPO has_changed{}; -#if defined( __clang__ ) && defined( __clang_minor__ ) -# pragma clang diagnostic pop -#endif +} // namespace detail UREACT_END_NAMESPACE @@ -3627,6 +3627,16 @@ class signal : protected detail::signal_internals template friend Ret detail::create_wrapped_node( Args&&... args ); + +private: + /*! + * @brief has_changed overload for @ref signal + */ + UREACT_WARN_UNUSED_RESULT friend constexpr bool has_changed( + const signal& lhs, const signal& rhs ) noexcept + { + return !lhs.equal_to( rhs ); + } }; /*! @@ -4644,7 +4654,7 @@ class signal_flatten_node final : public signal_node UREACT_WARN_UNUSED_RESULT update_result update() override { const auto& new_inner = get_internals( m_outer->value_ref() ).get_node_ptr(); - if( has_changed( new_inner, m_inner ) ) + if( new_inner != m_inner ) { // Topology has been changed auto old_inner = m_inner; @@ -4687,7 +4697,7 @@ class event_flatten_node final : public event_stream_node UREACT_WARN_UNUSED_RESULT update_result update() override { const auto& new_inner = get_internals( m_outer->value_ref() ).get_node_ptr(); - if( has_changed( new_inner, m_inner ) ) + if( new_inner != m_inner ) { // Topology has been changed auto old_inner = m_inner;