Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
aria::Command< Args > Class Template Reference

Encapsulated user action with an optional CanExecute predicate. More...

#include <command.hpp>

Public Types

using Action = std::function<void(Args...)>
using Predicate = std::function<bool(const Args&...)>
using CanExecuteSignal = detail::TypedSignal<bool>

Public Member Functions

template<std::invocable< Args... > A>
 Command (A &&action)
template<std::invocable< Args... > A, std::predicate< const Args &... > P>
 Command (A &&action, P &&predicate)
 Command (const Command &)=delete
Commandoperator= (const Command &)=delete
 Command (Command &&)=delete
Commandoperator= (Command &&)=delete
 ~Command ()
void execute (const Args &... args)
 Invoke the action if can_execute(args...) is true.
void operator() (const Args &... args)
bool can_execute (const Args &... args) const
void notify_can_execute_changed (const Args &... args) const
 Manually notify observers that can_execute may have changed.
Subscription observe_can_execute (std::function< void(bool)> fn)

Friends

class binding::BindingEngine

Detailed Description

template<typename... Args>
class aria::Command< Args >

Encapsulated user action with an optional CanExecute predicate.

Two flavours:

  • Command<> (parameterless) — the predicate is a () -> bool closure. On construction the command installs a reactive Effect that re-runs the predicate whenever any reactive value it reads changes, and emits a can_execute signal with the new truth value. Bound buttons auto-update enabled with no manual plumbing.
  • Command<Args...> (parameterised) — the predicate takes the same arguments the View supplies at click time. Because those arguments are only known at invocation, we cannot run the predicate eagerly to register reactive dependencies; callers that want enabled to reflect model state changes must call notify_can_execute_changed(args...) explicitly. (The BindingEngine::bind_command helper will also re-evaluate on demand for fixed args.)

Lifetime contract (READ BEFORE CAPTURING REFERENCES)

  1. Declare Command<> AFTER every Property / Computed its predicate reads. The no-arg specialisation runs the predicate once at construction to register its reactive dependency set; reading a not-yet-constructed member is undefined behaviour.
  2. Keep the predicate pure and side-effect free. The internal Effect may re-run it any time an upstream changes, and re-runs are coalesced by the reactive graph; observable side effects will appear non-deterministic.
  3. A Property freed before the Command it feeds is undefined behaviour — avoid it. Normal ViewModel usage co-owns both, so they die together at the owning scope's end. The reactive graph detaches the Effect's source edge when Property's ~Node runs, so the Command does not crash on destruction, but re-reading the predicate (via notify_can_execute_changed()) after an upstream was freed is UB (the predicate captures a reference to freed storage). See the test Command<>: Property freed before Command for the one case that IS safe: don't touch the command again, just let it destruct.

Member Typedef Documentation

◆ Action

template<typename... Args>
using aria::Command< Args >::Action = std::function<void(Args...)>

◆ Predicate

template<typename... Args>
using aria::Command< Args >::Predicate = std::function<bool(const Args&...)>

◆ CanExecuteSignal

template<typename... Args>
using aria::Command< Args >::CanExecuteSignal = detail::TypedSignal<bool>

Constructor & Destructor Documentation

◆ Command() [1/4]

template<typename... Args>
template<std::invocable< Args... > A>
aria::Command< Args >::Command ( A && action)
inlineexplicit

◆ Command() [2/4]

template<typename... Args>
template<std::invocable< Args... > A, std::predicate< const Args &... > P>
aria::Command< Args >::Command ( A && action,
P && predicate )
inline

◆ Command() [3/4]

template<typename... Args>
aria::Command< Args >::Command ( const Command< Args > & )
delete

◆ Command() [4/4]

template<typename... Args>
aria::Command< Args >::Command ( Command< Args > && )
delete

◆ ~Command()

template<typename... Args>
aria::Command< Args >::~Command ( )
inline

Member Function Documentation

◆ operator=() [1/2]

template<typename... Args>
Command & aria::Command< Args >::operator= ( const Command< Args > & )
delete

◆ operator=() [2/2]

template<typename... Args>
Command & aria::Command< Args >::operator= ( Command< Args > && )
delete

◆ execute()

template<typename... Args>
void aria::Command< Args >::execute ( const Args &... args)
inline

Invoke the action if can_execute(args...) is true.

◆ operator()()

template<typename... Args>
void aria::Command< Args >::operator() ( const Args &... args)
inline

◆ can_execute()

template<typename... Args>
bool aria::Command< Args >::can_execute ( const Args &... args) const
inlinenodiscard

◆ notify_can_execute_changed()

template<typename... Args>
void aria::Command< Args >::notify_can_execute_changed ( const Args &... args) const
inline

Manually notify observers that can_execute may have changed.

For Command<Args...> this is the primary mechanism (we cannot auto-track a predicate whose inputs we do not know). For Command<> callers normally never need to call this – the built-in Effect already re-evaluates automatically – but it is retained for force-refresh scenarios (e.g. external state that isn't expressed as a Property).

◆ observe_can_execute()

template<typename... Args>
Subscription aria::Command< Args >::observe_can_execute ( std::function< void(bool)> fn)
inlinenodiscard

◆ binding::BindingEngine

template<typename... Args>
friend class binding::BindingEngine
friend

The documentation for this class was generated from the following file: