去除所有访问级别
This commit is contained in:
+160
-155
@@ -4,65 +4,70 @@
|
||||
|
||||
## 1. Extension role
|
||||
|
||||
Structive extensions add domain-specific interpretation without changing the Core structural model.
|
||||
Extensions add domain metadata and domain interpretation without making Property Core depend on that domain.
|
||||
|
||||
The intended dependency is:
|
||||
The dependency direction is:
|
||||
|
||||
```text
|
||||
application / adapter
|
||||
↓
|
||||
Structive extension
|
||||
↓
|
||||
Structive Property Core
|
||||
Extension → Property Core
|
||||
Property Core -X→ Extension
|
||||
```
|
||||
|
||||
Core does not include or link extension code.
|
||||
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 normally add two things:
|
||||
An extension should own only its domain semantics.
|
||||
|
||||
1. one or more Attribute categories that express domain metadata;
|
||||
2. interpretation code that consumes those Attributes.
|
||||
For example, Presentation owns:
|
||||
|
||||
It should not create a parallel property registry when the existing `Object_Schema` already contains the required structure.
|
||||
|
||||
## 3. Current presentation extension
|
||||
|
||||
The current extension module defines four Attribute categories:
|
||||
|
||||
```cpp
|
||||
presentation::Label_Category
|
||||
presentation::Description_Category
|
||||
presentation::Group_Category
|
||||
presentation::Order_Category
|
||||
```text
|
||||
label
|
||||
description
|
||||
group
|
||||
order
|
||||
```
|
||||
|
||||
Convenience Attribute values:
|
||||
It does not need Property Core to understand presentation.
|
||||
|
||||
```cpp
|
||||
presentation::label<"Temperature">
|
||||
presentation::description<"Current device temperature">
|
||||
presentation::group<"Environment">
|
||||
presentation::order<10>
|
||||
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:
|
||||
|
||||
```text
|
||||
Should this property be visible?
|
||||
Should this endpoint permit a write?
|
||||
Should this property be persisted?
|
||||
Does the current user have permission?
|
||||
```
|
||||
|
||||
They can be attached directly to a normal Core property:
|
||||
An extension may inspect structural facts such as:
|
||||
|
||||
```cpp
|
||||
field<&Device::temperature>(
|
||||
key<"temperature">,
|
||||
unit<"C">,
|
||||
presentation::label<"Temperature">,
|
||||
presentation::description<"Current device temperature">,
|
||||
presentation::group<"Environment">,
|
||||
presentation::order<10>
|
||||
)
|
||||
```text
|
||||
Property::readable
|
||||
Property::writable
|
||||
sensitive metadata
|
||||
extension-owned metadata
|
||||
```
|
||||
|
||||
No wrapper such as `hint(...)` is required.
|
||||
but the final policy belongs to the consumer.
|
||||
|
||||
## 4. Presentation interpretation
|
||||
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:
|
||||
|
||||
@@ -76,192 +81,192 @@ Link:
|
||||
target_link_libraries(my_target PRIVATE structive::property_extensions)
|
||||
```
|
||||
|
||||
Describe a property by member pointer:
|
||||
Available Attributes:
|
||||
|
||||
```cpp
|
||||
presentation::label<"Temperature">
|
||||
presentation::description<"Current device temperature">
|
||||
presentation::group<"Environment">
|
||||
presentation::order<2>
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
field<&Device::temperature>(
|
||||
key<"temperature">,
|
||||
unit<"C">,
|
||||
presentation::label<"Temperature">,
|
||||
presentation::description<"Current device temperature">,
|
||||
presentation::group<"Environment">,
|
||||
presentation::order<2>
|
||||
)
|
||||
```
|
||||
|
||||
## 5. Presentation interpretation
|
||||
|
||||
```cpp
|
||||
auto info = presentation::describe<&Device::temperature>(device.schema());
|
||||
```
|
||||
|
||||
The result contains:
|
||||
`Presentation_Info` contains:
|
||||
|
||||
```cpp
|
||||
struct Presentation_Info {
|
||||
std::string_view key;
|
||||
std::string_view label;
|
||||
std::string_view description;
|
||||
std::string_view group;
|
||||
std::size_t order;
|
||||
bool has_order;
|
||||
};
|
||||
```text
|
||||
key
|
||||
label
|
||||
description
|
||||
group
|
||||
order
|
||||
has_order
|
||||
```
|
||||
|
||||
If no presentation label is declared, the extension uses the property key as the display label. This fallback is presentation policy and intentionally lives outside Core.
|
||||
If no label is supplied, the presentation extension falls back to the property key. That fallback is presentation behavior, not Core behavior.
|
||||
|
||||
## 5. Inheritable extension Attributes
|
||||
## 6. Inheritable extension Attributes
|
||||
|
||||
The presentation `group` Attribute is inheritable, so it may be placed in `defaults(...)`:
|
||||
An extension category may choose to be inheritable.
|
||||
|
||||
```cpp
|
||||
return object<Device>(
|
||||
defaults(
|
||||
external_access<External_Access::read>,
|
||||
presentation::group<"Environment">
|
||||
),
|
||||
field<&Device::temperature>(
|
||||
key<"temperature">,
|
||||
presentation::label<"Temperature">
|
||||
),
|
||||
field<&Device::pressure>(
|
||||
key<"pressure">,
|
||||
presentation::label<"Pressure">
|
||||
)
|
||||
);
|
||||
defaults(
|
||||
presentation::group<"Environment">
|
||||
)
|
||||
```
|
||||
|
||||
The extension resolves an effective declared value through the same Core default mechanism.
|
||||
Property-level metadata overrides an inheritable single-valued default from the same category.
|
||||
|
||||
`label`, `description` and `order` are not inheritable and therefore cannot be placed in `defaults(...)`.
|
||||
Use inheritance only for metadata whose semantics genuinely support inheritance.
|
||||
|
||||
## 6. Designing a new extension Attribute
|
||||
## 7. Designing a new extension Attribute
|
||||
|
||||
Example:
|
||||
A simple extension Attribute can be defined as:
|
||||
|
||||
```cpp
|
||||
namespace my_adapter {
|
||||
struct Json_Name_Category {};
|
||||
namespace my_extension {
|
||||
struct Format_Category {};
|
||||
template <Fixed_String Value>
|
||||
struct Json_Name_Attribute {
|
||||
using attribute_category = Json_Name_Category;
|
||||
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 Json_Name_Attribute<Value> json_name{};
|
||||
inline constexpr Format_Attribute<Value> format{};
|
||||
}
|
||||
```
|
||||
|
||||
Use it in a schema:
|
||||
Then attach it normally:
|
||||
|
||||
```cpp
|
||||
field<&Device::temperature>(
|
||||
key<"temperature">,
|
||||
my_adapter::json_name<"temp">
|
||||
my_extension::format<"0.0 C">
|
||||
)
|
||||
```
|
||||
|
||||
The Core stores it without needing to know what JSON means.
|
||||
Core does not require modification.
|
||||
|
||||
## 7. Reading extension Attributes
|
||||
## 8. Reading extension Attributes
|
||||
|
||||
For a property descriptor:
|
||||
Descriptor-local metadata:
|
||||
|
||||
```cpp
|
||||
using Property = std::remove_cvref_t<decltype(property)>;
|
||||
if constexpr (Property::has_attribute<my_adapter::Json_Name_Category>) {
|
||||
const auto& value = property.attribute<my_adapter::Json_Name_Category>();
|
||||
}
|
||||
using Property = std::remove_cvref_t<decltype(schema.property<&Device::temperature>())>;
|
||||
static_assert(Property::template has_attribute<my_extension::Format_Category>);
|
||||
```
|
||||
|
||||
For inheritable categories, use effective declared lookup:
|
||||
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.
|
||||
|
||||
## 10. Recommended extension boundaries
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
if constexpr (has_declared_effective_attribute_v<Schema, Index, My_Category>) {
|
||||
const auto& value = declared_effective_attribute<Index, My_Category>(schema);
|
||||
}
|
||||
Property::writable
|
||||
```
|
||||
|
||||
This checks the property declaration first and then object defaults.
|
||||
That is only eligibility. The editor may still apply additional application policy.
|
||||
|
||||
## 8. Multi-valued metadata
|
||||
A persistence adapter may choose not to store a property even when `Property::readable` is true.
|
||||
|
||||
Core uniqueness is category-driven. An Attribute with `single_valued = true` and a non-void `attribute_category` may only appear once in one declaration.
|
||||
An RPC endpoint may choose not to expose a writable property at all.
|
||||
|
||||
When a domain needs repeated annotations, design that metadata so it does not claim single-valued category uniqueness, then consume it through `for_each_attribute(...)`.
|
||||
This separation is intentional.
|
||||
|
||||
Do not force naturally repeated metadata into one large unrelated object merely to satisfy a single-valued design.
|
||||
## 12. Read-only optimization is a Core fact
|
||||
|
||||
## 9. Recommended extension boundaries
|
||||
Extensions do not need to implement special locking for `read_only` stored properties.
|
||||
|
||||
A future extension may reasonably own metadata and interpretation for areas such as:
|
||||
Core guarantees that such properties do not contribute lock slots or mutexes to the managed object and managed reads use the no-lock path.
|
||||
|
||||
- serialization naming and omission rules;
|
||||
- RPC exposure rules;
|
||||
- UI labels, groups and editor hints;
|
||||
- database column mapping;
|
||||
- configuration-file mapping;
|
||||
- domain documentation generation.
|
||||
An extension that deliberately performs raw C++ writes bypasses that guarantee and owns synchronization itself.
|
||||
|
||||
These are examples of extension domains, not currently implemented features.
|
||||
## 13. Fallback behavior belongs to the consumer
|
||||
|
||||
The important boundary is that Core should not gain a direct dependency on their libraries or domain types.
|
||||
|
||||
## 10. Core metadata may still be consumed
|
||||
|
||||
An extension is allowed to interpret Core-owned categories when those categories are part of the extension's input contract.
|
||||
|
||||
For example, an external adapter may use:
|
||||
|
||||
- property `key` as the default protocol name;
|
||||
- `External_Access` to determine exposure;
|
||||
- `sensitive` to decide whether its own logs should omit a value.
|
||||
|
||||
The extension may choose a policy based on those attributes, but it should not change what Core itself means by them.
|
||||
|
||||
## 11. Fallback behavior belongs to the consumer
|
||||
|
||||
The presentation extension demonstrates the intended rule:
|
||||
Core should not decide domain fallbacks such as:
|
||||
|
||||
```text
|
||||
no label Attribute
|
||||
↓
|
||||
presentation extension chooses property key as label
|
||||
missing label -> key
|
||||
missing database column -> key
|
||||
missing RPC name -> key
|
||||
```
|
||||
|
||||
Core does not invent that fallback because a different consumer may want a different behavior.
|
||||
Those rules belong to the corresponding extension or adapter.
|
||||
|
||||
The same principle should apply to future adapters. Defaults that exist only to make one domain pleasant should remain in that domain.
|
||||
## 14. Avoid semantic leakage into Core
|
||||
|
||||
## 12. Avoid extension-to-Core semantic leakage
|
||||
Do not add a Core enum or special branch only because one extension needs it.
|
||||
|
||||
Bad direction:
|
||||
Prefer:
|
||||
|
||||
```text
|
||||
JSON adapter needs alias support
|
||||
↓
|
||||
Core adds JSON_Alias_Category and JSON naming logic
|
||||
extension-owned Attribute category
|
||||
+ extension-owned interpretation
|
||||
```
|
||||
|
||||
Preferred direction:
|
||||
over:
|
||||
|
||||
```text
|
||||
JSON extension defines Json_Name_Category
|
||||
↓
|
||||
JSON extension interprets it
|
||||
↓
|
||||
Core remains domain-neutral
|
||||
Core learns the extension domain
|
||||
```
|
||||
|
||||
## 13. Linked code versus header-only metadata
|
||||
## 15. Linked code versus header-only metadata
|
||||
|
||||
Attribute definitions can often be header-only. Interpretation may be header-only or linked depending on implementation needs.
|
||||
Metadata types can usually remain header-only. Linked extension code is appropriate when a domain needs non-template runtime behavior.
|
||||
|
||||
The current presentation extension demonstrates a linked extension target. `presentation::describe()` performs compile-time Attribute selection and calls linked `make_presentation_info()` for the concrete result construction/fallback step.
|
||||
The current presentation extension keeps metadata in headers and provides `make_presentation_info` in the linked target.
|
||||
|
||||
Do not move linked implementation into Core merely because the extension is small.
|
||||
## 16. Extension review checklist
|
||||
|
||||
## 14. Extension review checklist
|
||||
Before adding an extension feature, ask:
|
||||
|
||||
Before accepting a new extension feature, ask:
|
||||
|
||||
1. Does this concept belong to Core or to one consumer domain?
|
||||
2. Can it be represented through the existing Attribute protocol?
|
||||
3. Does the extension own the Attribute category it interprets?
|
||||
4. Is an inheritable Attribute truly an object-wide default policy?
|
||||
5. Is fallback behavior kept inside the extension?
|
||||
6. Is the existing schema reused rather than duplicated?
|
||||
7. Does the dependency still point from extension to Core?
|
||||
8. Has Core remained free of third-party domain types?
|
||||
9. Does the extension preserve the existing meaning of Core access, validation and synchronization?
|
||||
|
||||
If the answer to the dependency or semantic-boundary questions is no, the layering should be redesigned before code is added.
|
||||
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?
|
||||
|
||||
Reference in New Issue
Block a user