From 063f23c75dcedb23b85d8a696630cdb4269e35bb Mon Sep 17 00:00:00 2001 From: vczh Date: Fri, 10 Apr 2026 01:30:12 -0700 Subject: [PATCH] Sync copilot instructions and knowledge base --- .github/KnowledgeBase/Index.md | 18 ++ ...KB_VlppReflection_AttributeRegistration.md | 154 ++++++++++++++++++ ...ppReflection_ClassInterfaceRegistration.md | 30 ++++ .../KB_VlppReflection_StructRegistration.md | 15 ++ .github/prompts/kb-sync.prompt.md | 38 +++++ 5 files changed, 255 insertions(+) create mode 100644 .github/KnowledgeBase/KB_VlppReflection_AttributeRegistration.md create mode 100644 .github/prompts/kb-sync.prompt.md diff --git a/.github/KnowledgeBase/Index.md b/.github/KnowledgeBase/Index.md index cc36c027..46642602 100644 --- a/.github/KnowledgeBase/Index.md +++ b/.github/KnowledgeBase/Index.md @@ -457,6 +457,7 @@ Registration patterns for structure types with field access capabilities. - Use `BEGIN_STRUCT_MEMBER` and `END_STRUCT_MEMBER` for struct registration - Use `STRUCT_MEMBER` to register each accessible field +- Use `ATTRIBUTE_TYPE`, `ATTRIBUTE_MEMBER` to attach attributes to the struct or its fields [API Explanation](./KB_VlppReflection_StructRegistration.md) @@ -481,6 +482,7 @@ Comprehensive registration system for classes and interfaces with methods, prope - Use `CLASS_MEMBER_PROPERTY_EVENT_READONLY_FAST`, `CLASS_MEMBER_PROPERTY_EVENT_FAST` for properties with change events - Use `NO_PARAMETER` for parameterless functions - Use `{ L"arg1" _ L"arg2" ... }` for parameter name lists +- Use `ATTRIBUTE_TYPE`, `ATTRIBUTE_MEMBER`, `ATTRIBUTE_PARAMETER` to attach attributes to types, members, and parameters [API Explanation](./KB_VlppReflection_ClassInterfaceRegistration.md) @@ -500,6 +502,22 @@ Proxy generation for interfaces to enable inheritance in Workflow scripts. [API Explanation](./KB_VlppReflection_InterfaceProxy.md) +#### Attribute Registration + +Attach metadata attributes to types, members, and method parameters during reflection registration. + +Attributes are instances of reflectable structs whose constructor arguments are serializable primitive values. +They are stored centrally in the owning type descriptor and can be queried at runtime via the `IAttributeBag` / `IAttributeInfo` interfaces. +Attributes survive metaonly metadata serialization and deserialization, and appear in the logged text output. + +- Use `ATTRIBUTE_TYPE(TYPE, ...)` to attach an attribute to the enclosing type descriptor +- Use `ATTRIBUTE_MEMBER(TYPE, ...)` to attach an attribute to the most recently registered member (field, property, event, method, or constructor) +- Use `ATTRIBUTE_PARAMETER(PARAMETER_NAME, TYPE, ...)` to attach an attribute to a named parameter of the most recently registered method or constructor +- Use `IAttributeBag::GetAttributeCount` and `IAttributeBag::GetAttribute` to query attributes at runtime +- Use `IAttributeInfo::GetAttributeType`, `IAttributeInfo::GetAttributeValueCount`, `IAttributeInfo::GetAttributeValue` to inspect attribute content + +[API Explanation](./KB_VlppReflection_AttributeRegistration.md) + ### Design Explanation ## VlppParser2 diff --git a/.github/KnowledgeBase/KB_VlppReflection_AttributeRegistration.md b/.github/KnowledgeBase/KB_VlppReflection_AttributeRegistration.md new file mode 100644 index 00000000..75141110 --- /dev/null +++ b/.github/KnowledgeBase/KB_VlppReflection_AttributeRegistration.md @@ -0,0 +1,154 @@ +# Attribute Registration + +## Attach metadata attributes to types, members, and method parameters during reflection registration. + +VlppReflection provides three macros for attaching attribute metadata to reflected types and their members. +An attribute is an instance of a reflectable struct whose constructor arguments are serializable primitive values (e.g., `WString`, `vint`, `bool`, `float`, `double`). +Attributes are stored centrally in the owning type descriptor and can be queried at runtime or through metaonly metadata. + +## Attribute Macros + +### ATTRIBUTE_TYPE(TYPE, ...) + +Attaches an attribute to the enclosing type descriptor. +Can appear anywhere inside a `BEGIN_*_MEMBER` / `END_*_MEMBER` block. +Zero or more arguments are allowed; `ATTRIBUTE_TYPE(MyAttr)` is equivalent to `MyAttr{}`. + +### ATTRIBUTE_MEMBER(TYPE, ...) + +Attaches an attribute to the most recently registered member (field, property, event, method, or constructor). +Must appear after at least one member registration macro (e.g., `CLASS_MEMBER_FIELD`, `CLASS_MEMBER_METHOD`, `CLASS_MEMBER_CONSTRUCTOR`, `CLASS_MEMBER_EVENT`, `CLASS_MEMBER_PROPERTY_*`, `STRUCT_MEMBER`). +Multiple `ATTRIBUTE_MEMBER` calls can follow the same member to attach multiple attributes. + +### ATTRIBUTE_PARAMETER(PARAMETER_NAME, TYPE, ...) + +Attaches an attribute to a named parameter of the most recently registered method or constructor. +`PARAMETER_NAME` is a wide string literal (e.g., `L"x"`) that must match exactly one parameter name. +Must appear after a method or constructor registration macro. + +## Attribute Type Requirements + +- The attribute type must be a reflected struct (`TypeDescriptorFlags::Struct`). +- It must be registered via `BEGIN_STRUCT_MEMBER` / `END_STRUCT_MEMBER` before use. +- `TYPE{ ARG1, ARG2, ... }` must be a valid C++ aggregate initialization expression. +- Each argument is boxed as a `Value` and its type descriptor must have a non-null `GetSerializableType()`. + +## Argument Type Inference + +The attribute macros use a C++20 `BoxingProxy` technique to infer each argument's type from the struct's field type rather than from the C++ deduced literal type. +During aggregate initialization of the attribute struct, each argument is wrapped in a `BoxingProxy` whose `operator FieldType()` is called by the compiler with `FieldType` deduced from the corresponding struct field. +This means: +- An integer literal like `1` written for a `vint` field is boxed as `vint` (= `vint64_t` on x64, `vint32_t` on Win32), not as `int`. +- A wide string literal like `L"name"` written for a `WString` field is boxed as `WString`, not as `const wchar_t*`. + +There is no need to explicitly cast arguments to the field type. + +## Storage Architecture + +All attributes are stored centrally in the owning `ITypeDescriptor`'s `AttributeBagSource` base class. +- `AttributeBagSource` uses `collections::Group>` where a `nullptr` key represents type-level attributes and non-null keys represent member/parameter-level attributes. +- `MemberInfoBase` (the base for all member info implementations) delegates `GetAttributeCount` / `GetAttribute` back to the owner type descriptor's `AttributeBagSource`. +- Attributes are returned in registration order. + +## Runtime Query + +Use the `IAttributeBag` interface (inherited by `ITypeDescriptor`, `IMemberInfo`, and `IParameterInfo`) to query attributes: +- `GetAttributeCount()` — returns the number of attributes on this target. +- `GetAttribute(index)` — returns the `IAttributeInfo` at the given index. + +Use `IAttributeInfo` to inspect an attribute: +- `GetAttributeType()` — returns the `ITypeDescriptor` of the attribute struct. +- `GetAttributeValueCount()` — returns the number of constructor argument values. +- `GetAttributeValue(index)` — returns the boxed `Value` of the argument at the given index. + +## Metaonly Metadata + +Attributes are serialized into metaonly binary metadata by `GenerateMetaonlyTypes` and deserialized by `LoadMetaonlyTypes`. +Each attribute value is serialized through `ISerializableType::Serialize` and deserialized through `ISerializableType::Deserialize`. +Attributes appear in the logged text output (`.txt` baseline files) in the format: +``` +@Attribute:(:, ...) +@ParamAttribute::(:, ...) +``` + +## Usage Example + +### Define attribute structs +```cpp +struct MyAttribute +{ + WString name; + vint number; + auto operator<=>(const MyAttribute&) const = default; +}; + +struct EmptyAttribute +{ + auto operator<=>(const EmptyAttribute&) const = default; +}; +``` + +### Register attribute structs +```cpp +BEGIN_STRUCT_MEMBER(MyAttribute) + STRUCT_MEMBER(name) + STRUCT_MEMBER(number) +END_STRUCT_MEMBER(MyAttribute) + +BEGIN_STRUCT_MEMBER(EmptyAttribute) +END_STRUCT_MEMBER(EmptyAttribute) +``` + +### Use attribute macros in struct registration +```cpp +BEGIN_STRUCT_MEMBER(MyStruct) + STRUCT_MEMBER(x) + ATTRIBUTE_MEMBER(MyAttribute, L"field-x", 42) + STRUCT_MEMBER(y) + ATTRIBUTE_MEMBER(EmptyAttribute) +END_STRUCT_MEMBER(MyStruct) +``` + +### Use attribute macros in class registration +```cpp +#define _ , + +BEGIN_CLASS_MEMBER(MyClass) + ATTRIBUTE_TYPE(MyAttribute, L"type", 1) + ATTRIBUTE_TYPE(EmptyAttribute) + + CLASS_MEMBER_CONSTRUCTOR(Ptr(vint), {L"seed"}) + ATTRIBUTE_MEMBER(MyAttribute, L"ctor", 2) + ATTRIBUTE_PARAMETER(L"seed", MyAttribute, L"param", 3) + + CLASS_MEMBER_EVENT(Changed) + ATTRIBUTE_MEMBER(MyAttribute, L"event", 4) + + CLASS_MEMBER_FIELD(fieldValue) + ATTRIBUTE_MEMBER(MyAttribute, L"field", 5) + + CLASS_MEMBER_PROPERTY_FAST(Value) + ATTRIBUTE_MEMBER(MyAttribute, L"property", 6) + + CLASS_MEMBER_METHOD(DoWork, {L"x" _ L"y"}) + ATTRIBUTE_MEMBER(MyAttribute, L"method", 7) + ATTRIBUTE_PARAMETER(L"x", MyAttribute, L"param-x", 8) + ATTRIBUTE_PARAMETER(L"y", EmptyAttribute) +END_CLASS_MEMBER(MyClass) + +#undef _ +``` + +## Compilation Level Support + +- Attribute macros are available when `VCZH_DEBUG_NO_REFLECTION` is **not** defined. +- They are excluded when `VCZH_DEBUG_NO_REFLECTION` is defined (no reflection mode). +- They work in both full reflection and metaonly reflection (`VCZH_DEBUG_METAONLY_REFLECTION`) builds. + +## Error Handling + +- `ATTRIBUTE_MEMBER` raises `CHECK_ERROR` if no member has been registered yet. +- `ATTRIBUTE_PARAMETER` raises `CHECK_ERROR` if the last registered member is not a method or constructor. +- `ATTRIBUTE_PARAMETER` raises `CHECK_ERROR` if the named parameter does not exist or is ambiguous. +- A `static_assert` fires at compile time if `TYPE{ ARG1, ... }` is not a valid expression. +- `CHECK_ERROR` is raised if the attribute type is not a reflected struct or if an argument is not serializable. diff --git a/.github/KnowledgeBase/KB_VlppReflection_ClassInterfaceRegistration.md b/.github/KnowledgeBase/KB_VlppReflection_ClassInterfaceRegistration.md index 68076269..2a54fa4f 100644 --- a/.github/KnowledgeBase/KB_VlppReflection_ClassInterfaceRegistration.md +++ b/.github/KnowledgeBase/KB_VlppReflection_ClassInterfaceRegistration.md @@ -92,6 +92,36 @@ END_CLASS_MEMBER(MyClass) #undef _ ``` +## Attribute Registration + +### Attaching Attributes to Types, Members, and Parameters +- Use `ATTRIBUTE_TYPE(TYPE, ...)` to attach an attribute to the type descriptor itself +- Use `ATTRIBUTE_MEMBER(TYPE, ...)` after any member registration to attach an attribute to that member +- Use `ATTRIBUTE_PARAMETER(PARAMETER_NAME, TYPE, ...)` after a method or constructor registration to attach an attribute to a named parameter +- The attribute type must be a reflected struct +- Each argument must be a serializable primitive value +- Multiple attributes can be attached to the same target +- See [Attribute Registration](./KB_VlppReflection_AttributeRegistration.md) for full details + +### Class with Attributes Example +```cpp +#define _ , + +BEGIN_CLASS_MEMBER(MyClass) + ATTRIBUTE_TYPE(MyAttribute, L"type", 1) + + CLASS_MEMBER_CONSTRUCTOR(Ptr(vint), {L"seed"}) + ATTRIBUTE_MEMBER(MyAttribute, L"ctor", 2) + ATTRIBUTE_PARAMETER(L"seed", MyAttribute, L"param", 3) + + CLASS_MEMBER_METHOD(DoWork, {L"x" _ L"y"}) + ATTRIBUTE_MEMBER(MyAttribute, L"method", 4) + ATTRIBUTE_PARAMETER(L"x", MyAttribute, L"param-x", 5) +END_CLASS_MEMBER(MyClass) + +#undef _ +``` + ## Interface Requirements ### Proxy Requirements diff --git a/.github/KnowledgeBase/KB_VlppReflection_StructRegistration.md b/.github/KnowledgeBase/KB_VlppReflection_StructRegistration.md index 46bdeb0b..cad76aca 100644 --- a/.github/KnowledgeBase/KB_VlppReflection_StructRegistration.md +++ b/.github/KnowledgeBase/KB_VlppReflection_StructRegistration.md @@ -23,6 +23,21 @@ END_STRUCT_MEMBER(MyStruct) - Each field registered becomes accessible through the reflection system - Supports both reading and writing field values dynamically +### Attribute Registration on Structs +- Use `ATTRIBUTE_TYPE(TYPE, ...)` to attach an attribute to the struct type itself +- Use `ATTRIBUTE_MEMBER(TYPE, ...)` after a `STRUCT_MEMBER` to attach an attribute to that field +- Multiple attributes can be attached to the same field +- See [Attribute Registration](./KB_VlppReflection_AttributeRegistration.md) for full details + +```cpp +BEGIN_STRUCT_MEMBER(MyStruct) + STRUCT_MEMBER(FirstField) + ATTRIBUTE_MEMBER(MyAttribute, L"first", 1) + STRUCT_MEMBER(SecondField) + ATTRIBUTE_MEMBER(EmptyAttribute) +END_STRUCT_MEMBER(MyStruct) +``` + ## Registration Requirements ### Struct Definition diff --git a/.github/prompts/kb-sync.prompt.md b/.github/prompts/kb-sync.prompt.md new file mode 100644 index 00000000..3c9a4023 --- /dev/null +++ b/.github/prompts/kb-sync.prompt.md @@ -0,0 +1,38 @@ +# Sync Knowledge Base with other Repos + +- Check out `Accessing Task Documents` and `Accessing Script Files` in `REPO-ROOT/.github/copilot-instructions.md` for context about mentioned `*.md` and `*.ps1` files. +- Following `Leveraging the Knowledge Base` in `REPO-ROOT/.github/copilot-instructions.md`, find knowledge and documents for this project in `REPO-ROOT/.github/KnowledgeBase/Index.md`. + +Here is a list of all repos, the `.github` folder should sync with each other: +- Vlpp +- VlppOS +- VlppRegex +- VlppReflection +- VlppParser2 +- Workflow +- GacUI +- Release +- Tools (instead of `.github` the folder calls `Copilot` in this repo) +There are also folder names, and `REPO-ROOT` is one of them. + +## Step 1. Sync back to Tools + +The `Tools` repo is the central place to maintain all instructions and documents. But knowledge bases are updated from each repo. So the first step is to copy knowledge base from the current repo back to `Tools`: +``` +& REPO-ROOT\..\Tools\Copilot\copilotInit.ps1 -UpdateKB +``` + +## Step 2. Sync from Tools to other Repos + +``` +& REPO-ROOT\..\Tools\Copilot\copilotInitAll.ps1 +``` + +## Step 3. Commit and Push + +After calling `copilotInit.ps1 -UpdateKB` and `copilotInitAll.ps1`, find out what repos are updated: +``` +& REPO-ROOT\..\Tools\Tools\CheckRepo.ps1 CheckAll +``` + +You will get a list, now go to each repo that has uncommitted files, commit all changes and push to the current branch of each repo.