morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
model_key.hpp File Reference
#include <charconv>
#include <concepts>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <type_traits>
#include <utility>

Go to the source code of this file.

Classes

struct  morph::model::ModelKeyTraits< Model >
 The key type of a model, when one has been declared for it. More...
struct  morph::model::ActionKeyTraits< Action >
 Declares where an action's model key comes from. More...

Concepts

concept  morph::model::RawModelKey
 Key types that are themselves a wire-encodable scalar.
concept  morph::model::WrappedModelKey
 Key types that wrap a RawModelKey — the ladder's strong ids.
concept  morph::model::ModelKey
 Key types a model may declare as its PrimaryKey.
concept  morph::model::KeyedModel
 Satisfied by model types that have a primary key, however it was named.

Macros

#define BRIDGE_MODEL_KEY(M, A, MEMBER)
 Declares that action A is the one that defines model M's primary key.
#define BRIDGE_KEY_FROM(A, MEMBER)
 Declares that action A also carries its model's primary key in MEMBER.
#define BRIDGE_MODEL_KEY_FROM_RESULT(M, A, MEMBER)
 Declares that action A's result establishes its model's primary key.
#define BRIDGE_KEY_FROM_RESULT(A, MEMBER)
 Declares that action A's result also establishes its model's key.

Typedefs

template<KeyedModel M>
using morph::model::PrimaryKeyOf = detail::KeyTypeOf<M>::type
 The primary key type of a keyed model.

Functions

template<ModelKey K>
std::string morph::model::keyToString (const K &key)
 Encodes a primary key as its canonical wire string.
template<ModelKey K>
K morph::model::keyFromString (std::string_view text)
 Decodes a primary key from its canonical wire string.

Detailed Description

Primary keys for model instances.

A model type declares itself keyed by exposing a nested PrimaryKey type alias; the alias is detected structurally (a requires-expression), never by inheritance or a marker base — the same detection style morph::views::ViewTraits uses for a view's kind/query members. A model without the alias is unkeyed and behaves exactly as it always has.

Actions say which of their fields carries that key via BRIDGE_KEY_FROM, or — for an action that creates the entity rather than naming it — which field of their result establishes it, via BRIDGE_KEY_FROM_RESULT. Actions with neither declaration are keyless and run against whichever instance the handler is already attached to; that is the common case.

Keys travel the wire as strings (wire::Envelope::primary) regardless of their C++ type, so the directory in RemoteServer needs exactly one map type rather than one per key type. See docs/spec/core/shared_instances.md.

Definition in file model_key.hpp.

Macro Definition Documentation

◆ BRIDGE_MODEL_KEY

#define BRIDGE_MODEL_KEY ( M,
A,
MEMBER )
Value:
template <> \
static constexpr bool hasKey = true; \
static constexpr bool fromResult = false; \
static std::string key(const A& action) { return morph::model::keyToString(action.*MEMBER); } \
}; \
template <> \
struct morph::model::ModelKeyTraits<M> { \
using PrimaryKey = morph::model::detail::MemberTypeOf<decltype(MEMBER)>; \
}
std::string keyToString(const K &key)
Encodes a primary key as its canonical wire string.
Declares where an action's model key comes from.

Declares that action A is the one that defines model M's primary key.

One line does both jobs, so the model's own class body needs to say nothing about keys: the key type is deduced from MEMBER's type (defining ModelKeyTraits<M>), and A is recorded as an action that carries it (defining ActionKeyTraits<A>).

Executing such an action on a shareable handler attaches — or re-points — that handler to the instance holding the named key, constructing one only if no instance holds it yet. Every keyless action on that handler afterwards lands on the same instance, which is what makes the common case free of ceremony:

BRIDGE_KEY_FROM(AccountModel, LoadAccount, &LoadAccount::id);
BridgeHandler<AccountModel, AllowShared> first{bridge, gui}, second{bridge, gui};
first .execute(LoadAccount{.id = 32}); // constructs the instance for 32
second.execute(LoadAccount{.id = 32}); // attaches to it; constructs nothing
first .execute(Deposit{.amount = 100}); // keyless -> instance 32
second.execute(GetBalance{}); // keyless -> instance 32, sees the 100
#define BRIDGE_KEY_FROM(A, MEMBER)
Declares that action A also carries its model's primary key in MEMBER.

MEMBER is a pointer-to-data-member of A (e.g. &LoadAccount::id) whose type satisfies morph::model::ModelKey. Must appear at global scope; a header included by several translation units is fine, like the other BRIDGE_REGISTER_* macros — this macro emits only explicit specialisations, which [basic.def.odr] permits in multiple translation units when the definitions are token-identical. See docs/spec/core/registry.md, "Header placement is legal".

Definition at line 292 of file model_key.hpp.

◆ BRIDGE_KEY_FROM

#define BRIDGE_KEY_FROM ( A,
MEMBER )
Value:
template <> \
static constexpr bool hasKey = true; \
static constexpr bool fromResult = false; \
static std::string key(const A& action) { return morph::model::keyToString(action.*MEMBER); } \
}

Declares that action A also carries its model's primary key in MEMBER.

The companion to BRIDGE_MODEL_KEY, for the other actions that name the same entity — CloseAccount{.id = ...} alongside GetAccount{.id = ...}. It records only that A carries the key; the key's type has already been established by the model's one BRIDGE_MODEL_KEY line, and an explicit specialisation cannot be repeated.

Must appear at global scope; a header included by several translation units is fine (only explicit specialisations are emitted — see docs/spec/core/registry.md, "Header placement is legal").

Definition at line 317 of file model_key.hpp.

◆ BRIDGE_MODEL_KEY_FROM_RESULT

#define BRIDGE_MODEL_KEY_FROM_RESULT ( M,
A,
MEMBER )
Value:
template <> \
static constexpr bool hasKey = true; \
static constexpr bool fromResult = true; \
template <typename R> \
static std::string keyOfResult(const R& result) { \
return morph::model::keyToString(result.*MEMBER); \
} \
}; \
template <> \
struct morph::model::ModelKeyTraits<M> { \
using PrimaryKey = morph::model::detail::MemberTypeOf<decltype(MEMBER)>; \
}

Declares that action A's result establishes its model's primary key.

For actions that create the entity rather than name it: the key is not in the request, it is generated and returned, exactly as a database insert returns its generated primary key. MEMBER is a pointer-to-data-member of A's result type (e.g. &AccountInfo::id).

Must appear at global scope; a header included by several translation units is fine (only explicit specialisations are emitted — see docs/spec/core/registry.md, "Header placement is legal").

Definition at line 337 of file model_key.hpp.

◆ BRIDGE_KEY_FROM_RESULT

#define BRIDGE_KEY_FROM_RESULT ( A,
MEMBER )
Value:
template <> \
static constexpr bool hasKey = true; \
static constexpr bool fromResult = true; \
template <typename R> \
static std::string keyOfResult(const R& result) { \
return morph::model::keyToString(result.*MEMBER); \
} \
}

Declares that action A's result also establishes its model's key.

The companion to BRIDGE_MODEL_KEY_FROM_RESULT, for a second creating action on a model whose key type is already established.

Must appear at global scope; a header included by several translation units is fine (only explicit specialisations are emitted — see docs/spec/core/registry.md, "Header placement is legal").

Definition at line 362 of file model_key.hpp.

Typedef Documentation

◆ PrimaryKeyOf

template<KeyedModel M>
using morph::model::PrimaryKeyOf = detail::KeyTypeOf<M>::type

The primary key type of a keyed model.

Template Parameters
MKeyed model type.

Definition at line 142 of file model_key.hpp.

Function Documentation

◆ keyToString()

template<ModelKey K>
std::string morph::model::keyToString ( const K & key)
nodiscard

Encodes a primary key as its canonical wire string.

Integral keys are decimal; std::string keys pass through unchanged; a strong id encodes as whatever it wraps, so it shares a directory entry with the raw key of the same value. The encoding is total for every engaged key — each has exactly one representation — so two clients naming the same key always land on the same directory entry.

Template Parameters
KKey type satisfying ModelKey.
Parameters
keyKey value to encode.
Returns
The canonical string form of key.
Exceptions
std::runtime_errorif key is a strong id with no value. Such an id names no instance, and encoding it as "" or "0" would route every caller holding an unset id to a single shared instance.

Definition at line 158 of file model_key.hpp.

◆ keyFromString()

template<ModelKey K>
K morph::model::keyFromString ( std::string_view text)
nodiscard

Decodes a primary key from its canonical wire string.

A strong id is reconstructed by decoding what it wraps and rewrapping it, so keyFromString<PasteId>(keyToString(id)) == id for any engaged id.

Template Parameters
KKey type satisfying ModelKey.
Parameters
textCanonical string form, as produced by keyToString.
Returns
The decoded key.
Exceptions
std::runtime_errorif text is not a valid encoding of a K (non-numeric text, trailing garbage, or a value out of range). A key that cannot be decoded is a protocol error, not a value to clamp: silently yielding 0 would route the caller to the wrong instance.

Definition at line 190 of file model_key.hpp.