去除所有访问级别

This commit is contained in:
2026-08-07 17:50:04 +08:00
parent 0c7fac70b2
commit 3205dd84e8
17 changed files with 2086 additions and 2424 deletions
+160 -155
View File
@@ -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?