api更新

This commit is contained in:
2026-08-07 20:20:46 +08:00
parent 3205dd84e8
commit caec91ae3f
13 changed files with 653 additions and 201 deletions
@@ -79,6 +79,7 @@ public:
}
};
}
/// Result of type-erased runtime property access. Runtime lookup never performs implicit type conversion.
enum class Runtime_Access_Result {
ok,
unknown_property,
@@ -86,7 +87,11 @@ enum class Runtime_Access_Result {
not_writable,
type_mismatch
};
/// Callback used by `runtime_read`.
/// The value pointer is valid only for the duration of the callback and must be consumed or copied synchronously. For synchronized writable state, the corresponding managed read lock remains held while the callback executes, so the callback must not re-enter a conflicting write on the same lock domain.
using Runtime_Read_Callback = void (*)(void*, std::size_t, std::string_view, const std::type_info&, const void*);
/// Type-erased boundary intended for adapters that discover a property key only at runtime.
/// Normal C++ business code should prefer `Property_Object::read/write`, which preserve compile-time type and capability checking.
class Property_Object_Base {
struct Runtime_Interface {
const std::type_info& (*object_type)() noexcept;
@@ -105,19 +110,26 @@ protected:
template <class Object, Synchronization_Policy Policy>
friend class Property_Object;
public:
/// Returns the concrete managed object type represented by this erased base.
const std::type_info& runtime_object_type() const noexcept {
return runtime_interface_->object_type();
}
/// Returns the number of properties in the concrete object's schema.
std::size_t runtime_property_count() const noexcept {
return runtime_interface_->property_count();
}
/// Looks up `key` at runtime and invokes `callback` exactly once on success.
/// The operation follows the same intrinsic readable capability and synchronization rules as typed `read`; stored read-only properties therefore keep their zero-lock fast path. `callback` is a required non-null function pointer.
Runtime_Access_Result runtime_read(std::string_view key, void* context, Runtime_Read_Callback callback) const {
return runtime_interface_->read(*this, key, context, callback);
}
/// Looks up `key` at runtime and performs the same managed write used by typed `write`.
/// `value_type` must exactly match the property's declared value type and `value` must point to a live object of that exact type for the duration of the call. No numeric, string or user-defined conversion is attempted.
Runtime_Access_Result runtime_write(std::string_view key, const std::type_info& value_type, const void* value) {
return runtime_interface_->write(*this, key, value_type, value);
}
};
/// Explicit per-instance synchronization override. The contained plan changes only lock topology; it does not change property capability or validation.
struct Property_Synchronization {
Synchronization_Plan plan;
};
@@ -129,6 +141,8 @@ Property_Synchronization property_synchronization(Source&& source) requires Prop
using Schema = type_descriptor_schema_t<Object>;
return {materialize_synchronization_plan<Schema>(std::forward<Source>(source))};
}
/// Non-owning inspection view of the effective synchronization topology for one object instance.
/// `unsynchronized_slot` means that managed access for the property owns no mutex domain. For stored read-only properties this is automatic.
struct Resolved_Synchronization_View {
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();
std::span<const std::size_t> lock_slots;
@@ -323,12 +337,10 @@ private:
public:
Single_Read_View(const Property_Object& owner, std::size_t property_index, std::size_t lock_slot) : owner_(&owner), property_index_(property_index), lock_slot_(lock_slot) {}
template <auto Member>
decltype(auto) get() const {
decltype(auto) get() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
using Property = typename Schema::template property_type<index>;
static_assert(Property::readable);
if constexpr (!Property::writable && !Property::accessor_type::synchronized_view_read) {
return owner_->template read_unlocked<index>(*this);
}
@@ -338,12 +350,10 @@ private:
return owner_->template read_unlocked<index>(*this);
}
template <Fixed_String Key>
decltype(auto) get_key() const {
decltype(auto) get() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
using Property = typename Schema::template property_type<index>;
static_assert(Property::readable);
if constexpr (!Property::writable && !Property::accessor_type::synchronized_view_read) {
return owner_->template read_unlocked<index>(*this);
}
@@ -413,27 +423,22 @@ private:
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(Schema::template property_type<Index>::readable);
decltype(auto) get_index() const requires Schema_Readable_Property_Index<type_descriptor_schema_t<Derived>, Index> {
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
decltype(auto) get() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
decltype(auto) get() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
};
@@ -460,51 +465,41 @@ private:
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(Schema::template property_type<Index>::readable);
decltype(auto) get_index() const requires Schema_Readable_Property_Index<type_descriptor_schema_t<Derived>, Index> {
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
decltype(auto) get() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
decltype(auto) get() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <std::size_t Index, class Value>
void set_index(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(Schema::template property_type<Index>::writable);
void set_index(Value&& value) requires Schema_Writable_Property_Index<type_descriptor_schema_t<Derived>, Index> {
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
owner_->template write_unlocked<Index>(std::forward<Value>(value));
}
template <auto Member, class Value>
void set(Value&& value) {
void set(Value&& value) requires Schema_Writable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void set_key(Value&& value) {
void set(Value&& value) requires Schema_Writable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
};
@@ -530,27 +525,22 @@ private:
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(Schema::template property_type<Index>::readable);
decltype(auto) get_index() const requires Schema_Readable_Property_Index<type_descriptor_schema_t<Derived>, Index> {
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
decltype(auto) get() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
decltype(auto) get() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
};
@@ -576,51 +566,41 @@ private:
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(Schema::template property_type<Index>::readable);
decltype(auto) get_index() const requires Schema_Readable_Property_Index<type_descriptor_schema_t<Derived>, Index> {
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
decltype(auto) get() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
decltype(auto) get() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <std::size_t Index, class Value>
void set_index(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(Schema::template property_type<Index>::writable);
void set_index(Value&& value) requires Schema_Writable_Property_Index<type_descriptor_schema_t<Derived>, Index> {
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
owner_->template write_unlocked<Index>(std::forward<Value>(value));
}
template <auto Member, class Value>
void set(Value&& value) {
void set(Value&& value) requires Schema_Writable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void set_key(Value&& value) {
void set(Value&& value) requires Schema_Writable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
};
@@ -778,6 +758,7 @@ public:
const auto& schema() const noexcept {
return type_descriptor<Derived>();
}
/// Returns the effective lock topology after intrinsic capability filtering and any per-instance override.
Resolved_Synchronization_View resolved_synchronization() const noexcept {
using Schema = type_descriptor_schema_t<Derived>;
if (custom_lock_layout_) {
@@ -796,30 +777,27 @@ public:
auto read() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return read_one<index>();
}
template <Fixed_String Key>
auto read_key() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
auto read() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return read_one<index>();
}
template <auto Member, class Value>
void write(Value&& value) requires Schema_Writable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
write_one<index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void write_key(Value&& value) requires Schema_Writable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
void write(Value&& value) requires Schema_Writable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
write_one<index>(std::forward<Value>(value));
}
/// Returns the resolved lock slot for a registered member. `unsynchronized_slot` means no mutex is created for that property.
template <auto Member>
std::size_t lock_slot() const noexcept {
using Schema = type_descriptor_schema_t<Derived>;
@@ -855,22 +833,26 @@ private:
return Static_Write_Guard<sizeof...(Members)>{*this, std::move(targets)};
}
public:
/// Runtime-key shared guard. Keys are validated at runtime; unknown or non-readable properties throw `std::invalid_argument`. Lock domains are deduplicated and acquired in stable slot order.
Read_Guard lock_shared(std::span<const std::string_view> keys) const {
return lock_shared_impl(keys);
}
Read_Guard lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared_impl(keys);
}
/// Runtime-key unique guard. Keys are validated at runtime; unknown or non-writable properties throw `std::invalid_argument`. Lock domains are deduplicated and acquired in stable slot order.
Write_Guard lock_unique(std::span<const std::string_view> keys) {
return lock_unique_impl(keys);
}
Write_Guard lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique_impl(keys);
}
/// Compile-time shared guard for registered readable members. Invalid or non-readable selections are removed by constraints before the function can be called.
template <auto... Members>
auto lock_shared() const requires (Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Members> && ...) {
return lock_shared_impl<Members...>();
}
/// Compile-time unique guard for registered writable members. The implementation sorts and deduplicates resolved slots so callers may request members in any order without introducing lock-order inversion.
template <auto... Members>
auto lock_unique() requires (Schema_Writable_Property_Member<type_descriptor_schema_t<Derived>, Members> && ...) {
return lock_unique_impl<Members...>();
@@ -201,6 +201,8 @@ template <class Schema, auto Member>
inline constexpr std::size_t schema_member_property_index_v = schema_member_property_index<Schema, Member>();
template <class Schema, auto Member>
concept Schema_Property_Member = Property_Schema<Schema> && std::is_member_object_pointer_v<decltype(Member)> && schema_member_property_index_v<Schema, Member> < Schema::property_count;
template <class Schema, std::size_t Index>
concept Schema_Property_Index = Property_Schema<Schema> && Index < Schema::property_count;
template <class Schema, std::size_t Index, class Category, class Fallback>
struct Effective_Attribute {
private:
@@ -234,6 +236,10 @@ template <class Schema>
concept Valid_Property_Schema = Property_Schema<Schema>;
template <class Schema, std::size_t Index>
inline constexpr bool property_requires_synchronization_v = Schema::template property_type<Index>::writable || Schema::template property_type<Index>::synchronized_view_read;
template <class Schema, std::size_t Index>
concept Schema_Readable_Property_Index = Schema_Property_Index<Schema, Index> && Schema::template property_type<Index>::readable;
template <class Schema, std::size_t Index>
concept Schema_Writable_Property_Index = Schema_Property_Index<Schema, Index> && Schema::template property_type<Index>::writable;
template <class Schema, auto Member>
concept Schema_Readable_Property_Member = Schema_Property_Member<Schema, Member> && Schema::template property_type<schema_member_property_index_v<Schema, Member>>::readable;
template <class Schema, auto Member>
@@ -12,14 +12,19 @@
#include <utility>
#include <vector>
namespace structive {
/// Defines the default lock-domain topology for properties that actually require managed synchronization.
/// Stored intrinsic read-only properties are filtered out later and never receive a lock slot, regardless of this default.
enum class Synchronization_Default {
independent,
shared,
unsynchronized
};
/// Changes the default topology used before per-property and group overrides are applied.
struct Sync_Default_Rule {
Synchronization_Default value{Synchronization_Default::independent};
};
/// Runtime-key override for one property. The key is resolved against the schema when the plan is materialized.
/// `independent` forces a dedicated domain; `unsynchronized` deliberately removes the property from real locking.
struct Sync_Property_Rule {
enum class Mode {
independent,
@@ -28,15 +33,19 @@ struct Sync_Property_Rule {
Mode mode{Mode::independent};
std::string property;
};
/// Runtime-key declaration that places multiple properties in one consistency domain.
/// Group membership affects only properties that require synchronization; intrinsic read-only stored properties remain lock-free.
struct Sync_Group_Rule {
std::string name;
std::vector<std::string> properties;
};
/// Compile-time member-pointer form of a single-property synchronization override.
template <auto Member, Sync_Property_Rule::Mode Mode>
struct Sync_Member_Rule {
static constexpr auto member = Member;
static constexpr auto mode = Mode;
};
/// Compile-time member-pointer form of a synchronization group. Member validity is checked against the target schema during materialization.
template <auto... Members>
struct Sync_Member_Group_Rule {
static_assert(sizeof...(Members) > 0);
@@ -71,6 +80,10 @@ Sync_Group_Rule sync_group(std::string_view name, Properties&&... properties) re
(rule.properties.emplace_back(std::string_view(std::forward<Properties>(properties))), ...);
return rule;
}
/// Runtime-materialized synchronization description.
/// A plan describes topology, not mutex objects: resolution converts keys and groups into compact lock slots for one schema.
/// Invalid references, duplicate property configuration, empty groups and duplicate group names are rejected while resolving the plan.
/// The schema default plan is resolved once per object type; a `Property_Synchronization` override resolves a separate compact layout only for that instance.
class Synchronization_Plan {
Synchronization_Default default_mode_{Synchronization_Default::independent};
std::vector<Sync_Property_Rule> property_rules_;
@@ -143,6 +156,8 @@ concept Synchronization_Spec_Type = requires {
};
template <class Type>
concept Synchronization_Source_Type = Synchronization_Plan_Type<Type> || Synchronization_Spec_Type<Type>;
/// Builds synchronization configuration without forcing every caller onto runtime strings.
/// If every rule is already runtime-shaped, this returns `Synchronization_Plan` directly. If any rule carries compile-time member pointers, it returns a typed specification that is later materialized against the concrete schema.
template <class... Rules>
auto synchronization(Rules&&... rules) {
if constexpr ((Runtime_Synchronization_Rule<Rules> && ...)) {
@@ -153,6 +168,9 @@ auto synchronization(Rules&&... rules) {
return Synchronization_Spec<std::decay_t<Rules>...>{{std::forward<Rules>(rules)...}};
}
}
/// Compact result after applying defaults, overrides, groups and intrinsic property capabilities.
/// `lock_slots[i]` identifies the mutex domain for property `i`; `unsynchronized_slot` means no mutex exists for that property.
/// `lock_count` is the number of actual logical mutex domains after read-only filtering and group deduplication.
template <std::size_t Property_Count>
struct Resolved_Synchronization_Plan {
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();