AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
APropertyPrecomputed< T > Class Template Referencefinal

Readonly property that holds a value computed by an expression. More...

#include <AUI/Common/APropertyPrecomputed.h>

Detailed Description

template<typename T>
class APropertyPrecomputed< T >
Warning
This API is experimental. Experimental APIs are likely to contain bugs, might be changed or removed in the future.
APropertyPrecomputed<T> is a readonly property similar to AProperty<T>. It holds an instance of T as well. Its value is determined by the C++ function specified in its constructor, typically a C++ lambda expression.

See property system for usage info.

Despite properties offer projection methods, you might want to track and process values of several properties.

APropertyPrecomputed<T> is a readonly property similar to AProperty<T>. It holds an instance of T as well. Its value is determined by a reactive expression specified in APropertyPrecomputed<T>'s constructor, typically a C++ lambda.

It's convenient to access values from another properties inside the expression. The properties accessed during invocation of the expression are tracked behind the scenes so they become dependencies of APropertyPrecomputed automatically. If one of the tracked properties fires changed signal, APropertyPrecomputed invalidates its T. APropertyPrecomputed follows lazy semantics so the expression is re-evaluated and the new result is applied to APropertyPrecomputed as soon as the latter is accessed for the next time.

In other words, it allows to specify relationships between different object properties and reactively update APropertyPrecomputed value whenever its dependencies change. APropertyPrecomputed<T> is somewhat similar to Qt Bindable Properties.

APropertyPrecomputed is a readonly property, hence you can't update its value with assignment. You can get its value with value() method or implicit conversion operator T() as with other properties.

Declaration#

Declare a property with custom expression determining it's value as follows:

struct User {
    AProperty<AString> name;
    AProperty<AString> surname;
    APropertyPrecomputed<AString> fullName = [&] { return "{} {}"_format(name, surname); };
};
Basic easy-to-use property implementation containing T.
Definition AProperty.h:30

Let's make little observer object for demonstration:

class LogObserver : public AObject {
public:
    void log(const AString& msg) {
        ALogger::info("LogObserver") << "Received value: " << msg;
    }
};
A base object class.
Definition AObject.h:39
Represents a Unicode character string.
Definition AString.h:38

Usage:

auto u = aui::ptr::manage_shared(new User {
    .name = "Emma",
    .surname = "Watson",
});
auto observer = _new<LogObserver>();
EXPECT_CALL(*observer, log(AString("Emma Watson"))).Times(1);
AObject::connect(u->fullName, AUI_SLOT(observer)::log);
#define AUI_SLOT(v)
Passes some variable and type of the variable separated by comma. It's convenient to use with the con...
Definition kAUI.h:88
static decltype(auto) connect(const Signal &signal, Object *object, Function &&function)
Connects signal to the slot of the specified object.
Definition AObject.h:82
static _< T > manage_shared(T *raw)
Delegates memory management of the raw pointer T* raw to the shared pointer, which is returned.
Definition SharedPtrTypes.h:460

The example above prints "Emma Watson". If we try to update one of dependencies of APropertyPrecomputed (i.e., name or surname), APropertyPrecomputed responds immediately:

EXPECT_CALL(*observer, log(AString("Emma Stone"))).Times(1);
u->surname = "Stone";

The example above prints "Emma Stone".

Valid Expressions#

Any C++ callable evaluating to T can be used as an expression for APropertyPrecomputed<T>. However, to formulate correct expression, some rules must be satisfied.

Dependency tracking only works on other properties. It is the developer's responsibility to ensure all values referenced in the expression are properties, or, at least, non-property values that wouldn't change or whose changes are not interesting. You definitely can use branching inside the expression, but you must be confident about what are you doing. Generally speaking, use as trivial expressions as possible.

struct User {
    AProperty<AString> name;
    AProperty<AString> surname;
    APropertyPrecomputed<AString> fullName = [&]() -> AString {
        if (name->empty()) {
            return "-";
        }
        if (surname->empty()) {
            return "-";
        }
        return "{} {}"_format(name, surname);
    };
};

In this expression, we have a fast path return if name is empty.

User u = {
    .name = "Emma",
    .surname = "Watson",
};
// trivial: we've accessed all referenced properties
EXPECT_EQ(u.fullName, "Emma Watson");

As soon as we set name to "", we don't access surname. If we try to trigger the fast path return:

u.name = "";

surname can't trigger re-evaluation anyhow. Re-evaluation can be triggered by name only. So, at the moment, we are interested in name changes only.

APropertyPrecomputed might evaluate its expression several times during its lifetime. The developer must make sure that all objects referenced in the expression live longer than APropertyPrecomputed.

The expression should not read from the property it's a binding for, including other referenced APropertyPrecomputes. Otherwise, there's an infinite evaluation loop, and AEvaluationLoopException is thrown.

Copying and moving APropertyPrecomputed#

Warning
Despite the underlying value and factory callback are both copy constructible and movable, the copy and move constructor are explicitly deleted to avoid potential object lifetime errors created by the lambda capture and prevent non-intuitive behaviour.
struct User {
    AProperty<AString> name;
    AProperty<AString> surname;
    APropertyPrecomputed<AString> fullName = [&] { return "{} {}"_format(name, surname); };
};

If copy construction of APropertyPrecomputed were possible, consider the following code:

User user { .name = "Hello" };
auto copy = user;             // WON'T COMPILE
auto moved = std::move(user); // WON'T COMPILE

copy has copied factory function of user, which refers to fields of user, not to copy's fields. Copy construction of a class or struct discards default values of all fields - this is the way APropertyPrecomputed's factory function is set to APropertyPrecomputed.

Examples
examples/7guis/cells/src/Cell.h, examples/7guis/crud/src/main.cpp, and examples/7guis/timer/src/main.cpp.

Member Function Documentation

◆ invalidate()

template<typename T>
void APropertyPrecomputed< T >::invalidate ( )
inlineoverridevirtual

In common, you won't need to use this function. APropertyPrecomputed is reevaluated automatically as soon as one updates a property expression depends on.

Implements aui::react::DependencyObserver.