Files
2026-08-07 17:50:04 +08:00

6.6 KiB

Structive Extension Architecture

中文

1. Extension role

Extensions add domain metadata and domain interpretation without making Property Core depend on that domain.

The dependency direction is:

Extension → Property Core
Property Core -X→ Extension

Core supplies:

  • schema and property descriptors;
  • intrinsic readable/writable capability;
  • the Attribute protocol;
  • constraints;
  • synchronization metadata and managed access;
  • runtime structural access.

Extensions add their own categories and interpretation.

2. Extension principle

An extension should own only its domain semantics.

For example, Presentation owns:

label
description
group
order

It does not need Property Core to understand presentation.

Likewise, a future persistence extension may own persistence-specific metadata, and an RPC extension may own protocol metadata.

3. External policy stays external

Property Core has no built-in access-control mode.

An extension or application layer is responsible for its own policy:

Should this property be visible?
Should this endpoint permit a write?
Should this property be persisted?
Does the current user have permission?

An extension may inspect structural facts such as:

Property::readable
Property::writable
sensitive metadata
extension-owned metadata

but the final policy belongs to the consumer.

A read_only property means the Structive managed object itself has no write operation for that property. It does not mean every external system must expose it.

4. Current presentation extension

Include:

#include <structive/property/extensions/presentation.hpp>

Link:

target_link_libraries(my_target PRIVATE structive::property_extensions)

Available Attributes:

presentation::label<"Temperature">
presentation::description<"Current device temperature">
presentation::group<"Environment">
presentation::order<2>

Example:

field<&Device::temperature>(
    key<"temperature">,
    unit<"C">,
    presentation::label<"Temperature">,
    presentation::description<"Current device temperature">,
    presentation::group<"Environment">,
    presentation::order<2>
)

5. Presentation interpretation

auto info = presentation::describe<&Device::temperature>(device.schema());

Presentation_Info contains:

key
label
description
group
order
has_order

If no label is supplied, the presentation extension falls back to the property key. That fallback is presentation behavior, not Core behavior.

6. Inheritable extension Attributes

An extension category may choose to be inheritable.

defaults(
    presentation::group<"Environment">
)

Property-level metadata overrides an inheritable single-valued default from the same category.

Use inheritance only for metadata whose semantics genuinely support inheritance.

7. Designing a new extension Attribute

A simple extension Attribute can be defined as:

namespace my_extension {
struct Format_Category {};
template <Fixed_String Value>
struct Format_Attribute {
    using attribute_category = Format_Category;
    static constexpr bool single_valued = true;
    static constexpr bool inheritable = false;
    static constexpr auto value = Value;
};
template <Fixed_String Value>
inline constexpr Format_Attribute<Value> format{};
}

Then attach it normally:

field<&Device::temperature>(
    key<"temperature">,
    my_extension::format<"0.0 C">
)

Core does not require modification.

8. Reading extension Attributes

Descriptor-local metadata:

using Property = std::remove_cvref_t<decltype(schema.property<&Device::temperature>())>;
static_assert(Property::template has_attribute<my_extension::Format_Category>);

Effective inheritable metadata can use Core's generic effective-Attribute utilities.

The extension owns fallback and interpretation rules.

9. Multi-valued metadata

If a category is not single-valued, an extension may allow multiple Attributes from that category.

Do not mark a category single_valued unless duplicate declarations are structurally invalid.

Good extension domains include:

  • presentation and editor hints;
  • serialization naming and format hints;
  • persistence mapping metadata;
  • RPC/protocol naming metadata;
  • diagnostics and telemetry metadata;
  • documentation metadata.

The domain implementation should remain outside Property Core.

11. Using intrinsic capability

A consumer may use intrinsic capability as a structural fact.

For example, an editor can decide that a property is only eligible for an editable widget when:

Property::writable

That is only eligibility. The editor may still apply additional application policy.

A persistence adapter may choose not to store a property even when Property::readable is true.

An RPC endpoint may choose not to expose a writable property at all.

This separation is intentional.

12. Read-only optimization is a Core fact

Extensions do not need to implement special locking for read_only stored properties.

Core guarantees that such properties do not contribute lock slots or mutexes to the managed object and managed reads use the no-lock path.

An extension that deliberately performs raw C++ writes bypasses that guarantee and owns synchronization itself.

13. Fallback behavior belongs to the consumer

Core should not decide domain fallbacks such as:

missing label -> key
missing database column -> key
missing RPC name -> key

Those rules belong to the corresponding extension or adapter.

14. Avoid semantic leakage into Core

Do not add a Core enum or special branch only because one extension needs it.

Prefer:

extension-owned Attribute category
+ extension-owned interpretation

over:

Core learns the extension domain

15. Linked code versus header-only metadata

Metadata types can usually remain header-only. Linked extension code is appropriate when a domain needs non-template runtime behavior.

The current presentation extension keeps metadata in headers and provides make_presentation_info in the linked target.

16. Extension review checklist

Before adding an extension feature, ask:

  1. Does Core already provide the required generic structural mechanism?
  2. Can the feature be expressed as an extension-owned Attribute?
  3. Is its fallback rule owned by the extension?
  4. Is access/exposure policy being kept outside Core?
  5. Is intrinsic readable/writable being treated as structure rather than authorization?
  6. Does the extension avoid raw writes that would bypass managed synchronization unless intentional?
  7. Does Core remain independent of the extension?