This commit is contained in:
2026-08-12 06:16:00 +08:00
parent a672f37fae
commit 42392bbb7e
10 changed files with 246 additions and 10 deletions
+126 -10
View File
@@ -82,21 +82,52 @@ struct No_Defaults {
std::tuple<> attributes;
};
inline constexpr No_Defaults no_defaults{};
template <class Object, class Defaults, class... Properties>
template <class... Attributes>
struct Type_Metadata {
using type_metadata_tag = void;
using attribute_types = Type_List<Attributes...>;
static_assert((Property_Attribute<Attributes> && ...));
static_assert(unique_single_value_categories<Attributes...>());
std::tuple<Attributes...> attributes;
};
template <class Type>
concept Type_Metadata_Type = requires {
typename Type::type_metadata_tag;
typename Type::attribute_types;
};
template <class... Attributes>
constexpr auto type_metadata(Attributes&&... attributes) {
return Type_Metadata<std::decay_t<Attributes>...>{
{std::forward<Attributes>(attributes)...}};
}
using No_Type_Metadata = Type_Metadata<>;
inline constexpr No_Type_Metadata no_type_metadata{};
template <class Object, class Defaults, class Type_Metadata_Value, class... Properties>
class Object_Schema {
[[no_unique_address]] Defaults object_defaults_;
[[no_unique_address]] Type_Metadata_Value type_metadata_;
Synchronization_Plan synchronization_plan_;
std::tuple<Properties...> properties_;
public:
using property_schema_tag = void;
using object_type = Object;
using defaults_type = Defaults;
using type_metadata_type = Type_Metadata_Value;
using type_attribute_types = typename Type_Metadata_Value::attribute_types;
using property_types = Type_List<Properties...>;
static constexpr std::size_t property_count = sizeof...(Properties);
static_assert((std::same_as<typename Properties::object_type, Object> && ...));
static_assert(unique_property_keys<Properties...>());
static_assert(unique_property_storage<Properties...>());
Object_Schema(Defaults object_defaults, Synchronization_Plan synchronization_plan, std::tuple<Properties...> properties) : object_defaults_(std::move(object_defaults)), synchronization_plan_(std::move(synchronization_plan)), properties_(std::move(properties)) {}
Object_Schema(Defaults object_defaults, Type_Metadata_Value type_metadata_value,
Synchronization_Plan synchronization_plan,
std::tuple<Properties...> properties)
: object_defaults_(std::move(object_defaults)),
type_metadata_(std::move(type_metadata_value)),
synchronization_plan_(std::move(synchronization_plan)),
properties_(std::move(properties)) {}
Object_Schema(const Object_Schema&) = default;
Object_Schema(Object_Schema&&) noexcept = default;
Object_Schema& operator=(const Object_Schema&) = delete;
@@ -106,6 +137,40 @@ public:
const Defaults& object_defaults() const noexcept {
return object_defaults_;
}
const Type_Metadata_Value& metadata() const noexcept {
return type_metadata_;
}
template <class Category>
using type_attribute_type = find_attribute_in_list_t<Category, type_attribute_types>;
template <class Category>
static constexpr std::size_t type_attribute_count =
[]<class... Attributes>(Type_List<Attributes...>) {
return count_attribute_category<Category, Attributes...>();
}(type_attribute_types{});
template <class Category>
static constexpr bool has_type_attribute = type_attribute_count<Category> != 0;
template <class Category>
constexpr decltype(auto) type_attribute() const
requires (type_attribute_count<Category> == 1) {
using target = type_attribute_type<Category>;
return std::get<target>(type_metadata_.attributes);
}
template <class Function>
constexpr void for_each_type_attribute(Function&& function) const {
std::apply([&](const auto&... attributes) {
(function(attributes), ...);
}, type_metadata_.attributes);
}
template <class Category, class Function>
constexpr void for_each_type_attribute(Function&& function) const {
std::apply([&](const auto&... attributes) {
([&] {
using type = std::remove_cvref_t<decltype(attributes)>;
if constexpr (std::same_as<attribute_category_of_t<type>, Category>)
function(attributes);
}(), ...);
}, type_metadata_.attributes);
}
template <auto Selector>
constexpr const auto& property() const {
if constexpr (std::integral<decltype(Selector)>) {
@@ -148,6 +213,8 @@ concept Property_Schema = requires {
typename Schema::property_schema_tag;
typename Schema::object_type;
typename Schema::defaults_type;
typename Schema::type_metadata_type;
typename Schema::type_attribute_types;
typename Schema::property_types;
{ Schema::property_count } -> std::convertible_to<std::size_t>;
};
@@ -527,33 +594,82 @@ bool visit_schema_property(const Schema& schema, std::string_view key_value, Fun
}
return visit_schema_property_at(schema, *index, std::forward<Function>(function));
}
template <class Object, class Defaults, class Source, class... Properties>
auto make_object_schema(Defaults&& object_defaults, Source&& synchronization_source, Properties&&... properties) requires Synchronization_Source_Type<std::remove_cvref_t<Source>> {
using schema_type = Object_Schema<Object, std::decay_t<Defaults>, std::decay_t<Properties>...>;
template <class Object, class Defaults, class Metadata, class Source, class... Properties>
auto make_object_schema(Defaults&& object_defaults, Metadata&& metadata,
Source&& synchronization_source, Properties&&... properties)
requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> &&
Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
Synchronization_Source_Type<std::remove_cvref_t<Source>> {
using schema_type = Object_Schema<Object, std::decay_t<Defaults>,
std::decay_t<Metadata>, std::decay_t<Properties>...>;
constexpr bool valid_schema = Valid_Property_Schema<schema_type>;
static_assert(valid_schema, "property dependency graph must be valid and acyclic");
auto plan = materialize_synchronization_plan<schema_type>(std::forward<Source>(synchronization_source));
schema_type schema{std::forward<Defaults>(object_defaults), std::move(plan), std::tuple<std::decay_t<Properties>...>{std::forward<Properties>(properties)...}};
schema_type schema{std::forward<Defaults>(object_defaults),
std::forward<Metadata>(metadata), std::move(plan),
std::tuple<std::decay_t<Properties>...>{
std::forward<Properties>(properties)...}};
if constexpr (valid_schema) {
static_cast<void>(resolve_synchronization_plan(schema, schema.synchronization_plan()));
}
return schema;
}
template <class Object, class Defaults, class Metadata, class Source, class... Properties>
auto object_schema(Defaults&& object_defaults, Metadata&& metadata,
Source&& synchronization_source, Properties&&... properties)
requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> &&
Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
Synchronization_Source_Type<std::remove_cvref_t<Source>> &&
(Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults),
std::forward<Metadata>(metadata),
std::forward<Source>(synchronization_source),
std::forward<Properties>(properties)...);
}
template <class Object, class Metadata, class Source, class... Properties>
auto object_schema(Metadata&& metadata, Source&& synchronization_source,
Properties&&... properties)
requires Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
Synchronization_Source_Type<std::remove_cvref_t<Source>> &&
(Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, std::forward<Metadata>(metadata),
std::forward<Source>(synchronization_source),
std::forward<Properties>(properties)...);
}
template <class Object, class Defaults, class Source, class... Properties>
auto object_schema(Defaults&& object_defaults, Source&& synchronization_source, Properties&&... properties) requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> && Synchronization_Source_Type<std::remove_cvref_t<Source>> && (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults), std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
return make_object_schema<Object>(std::forward<Defaults>(object_defaults), no_type_metadata, std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
}
template <class Object, class Source, class... Properties>
auto object_schema(Source&& synchronization_source, Properties&&... properties) requires Synchronization_Source_Type<std::remove_cvref_t<Source>> && (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
return make_object_schema<Object>(no_defaults, no_type_metadata, std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
}
template <class Object, class Defaults, class Metadata, class... Properties>
auto object_schema(Defaults&& object_defaults, Metadata&& metadata,
Properties&&... properties)
requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> &&
Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
(Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults),
std::forward<Metadata>(metadata),
Synchronization_Plan{},
std::forward<Properties>(properties)...);
}
template <class Object, class Metadata, class... Properties>
auto object_schema(Metadata&& metadata, Properties&&... properties)
requires Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
(Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, std::forward<Metadata>(metadata),
Synchronization_Plan{},
std::forward<Properties>(properties)...);
}
template <class Object, class Defaults, class... Properties>
auto object_schema(Defaults&& object_defaults, Properties&&... properties) requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> && (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults), Synchronization_Plan{}, std::forward<Properties>(properties)...);
return make_object_schema<Object>(std::forward<Defaults>(object_defaults), no_type_metadata, Synchronization_Plan{}, std::forward<Properties>(properties)...);
}
template <class Object, class... Properties>
auto object_schema(Properties&&... properties) requires (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, Synchronization_Plan{}, std::forward<Properties>(properties)...);
return make_object_schema<Object>(no_defaults, no_type_metadata, Synchronization_Plan{}, std::forward<Properties>(properties)...);
}
template <class Object, class... Arguments>
requires requires(Arguments&&... arguments) {
+1
View File
@@ -78,6 +78,7 @@ if(STRUCTIVE_BUILD_TESTS)
structive_expect_compile_failure(incompatible_constraint "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/incompatible_constraint.cpp")
structive_expect_compile_failure(foreign_sync_member "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/foreign_sync_member.cpp")
structive_expect_compile_failure(duplicate_attribute "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/duplicate_attribute.cpp")
structive_expect_compile_failure(duplicate_type_attribute "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/duplicate_type_attribute.cpp")
structive_expect_compile_failure(missing_computed_dependency "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/missing_computed_dependency.cpp")
structive_expect_compile_failure(undeclared_computed_read "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/undeclared_computed_read.cpp")
structive_expect_compile_failure(dependency_cycle "${CMAKE_CURRENT_LIST_DIR}/tests/compile_fail/dependency_cycle.cpp")
@@ -0,0 +1,17 @@
#include <structive/property/property.hpp>
struct Device {};
struct Type_Name_Category {};
template <int Value>
struct Type_Name {
using attribute_category = Type_Name_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = false;
};
int main() {
auto schema = structive::object<Device>(
structive::type_metadata(Type_Name<1>{}, Type_Name<2>{}));
static_cast<void>(schema);
}
+30
View File
@@ -24,6 +24,23 @@ struct Test_Multi_Attribute {
};
template <int Value>
inline constexpr Test_Multi_Attribute<Value> test_multi{};
struct Test_Type_Action_Category {};
struct Test_Type_Action_Attribute {
using attribute_category = Test_Type_Action_Category;
static constexpr bool single_valued = false;
static constexpr bool inheritable = false;
std::string_view name;
};
inline constexpr auto test_type_action(std::string_view name) {
return Test_Type_Action_Attribute{name};
}
struct Test_Type_Name_Category {};
struct Test_Type_Name_Attribute {
using attribute_category = Test_Type_Name_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = false;
std::string_view value;
};
struct Device : Property_Object<Device> {
Device() = default;
explicit Device(Property_Synchronization synchronization) : Property_Object(std::move(synchronization)) {}
@@ -37,6 +54,8 @@ template <>
struct structive::Type_Descriptor<Device> {
static auto get() {
return object<Device>(
type_metadata(Test_Type_Name_Attribute{"device"},
test_type_action("calibrate"), test_type_action("reset")),
synchronization(sync_all_independent, sync_group<&Device::min_speed, &Device::max_speed>("speed_range")),
field<&Device::temperature>(key<"temperature">, min_value<-50>, max_value<200>, unit<"C">, test_tag<7>, test_multi<3>, test_multi<5>),
field<&Device::pressure>(key<"pressure">),
@@ -207,6 +226,9 @@ int main() {
using Schema = type_descriptor_schema_t<Device>;
static_assert(Valid_Property_Schema<Schema>);
static_assert(Schema::property_count == 5);
static_assert(Schema::template type_attribute_count<Test_Type_Action_Category> == 2);
static_assert(Schema::template has_type_attribute<Test_Type_Action_Category>);
static_assert(Schema::template type_attribute_count<Test_Type_Name_Category> == 1);
using Immutable_Property = typename Schema::template property_type<4>;
static_assert(Immutable_Property::readable);
static_assert(!Immutable_Property::writable);
@@ -214,6 +236,14 @@ int main() {
REQUIRE(schema.template property<&Device::temperature>().key() == "temperature");
REQUIRE(schema.template property<&Device::min_speed>().key() == "minimum_speed");
REQUIRE(schema.template property<&Device::max_speed>().key() == "maximum_speed");
std::string type_actions;
schema.for_each_type_attribute<Test_Type_Action_Category>([&](const auto& action) {
if (!type_actions.empty())
type_actions += ',';
type_actions += action.name;
});
REQUIRE(type_actions == "calibrate,reset");
REQUIRE(schema.type_attribute<Test_Type_Name_Category>().value == "device");
using Temperature_Property = std::remove_cvref_t<decltype(schema.template property<&Device::temperature>())>;
static_assert(Temperature_Property::template has_attribute<Test_Tag_Category>);
static_assert(Temperature_Property::template attribute_count<Test_Tag_Category> == 1);