7.0 KiB
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_MEMBERbefore use. TYPE{ ARG1, ARG2, ... }must be a valid C++ aggregate initialization expression.- Each argument is boxed as a
Valueand its type descriptor must have a non-nullGetSerializableType().
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
1written for avintfield is boxed asvint(=vint64_ton x64,vint32_ton Win32), not asint. - A wide string literal like
L"name"written for aWStringfield is boxed asWString, not asconst 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.
AttributeBagSourceusescollections::Group<IMemberInfo*, Ptr<IAttributeInfo>>where anullptrkey represents type-level attributes and non-null keys represent member/parameter-level attributes.MemberInfoBase(the base for all member info implementations) delegatesGetAttributeCount/GetAttributeback to the owner type descriptor'sAttributeBagSource.- 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 theIAttributeInfoat the given index.
Use IAttributeInfo to inspect an attribute:
GetAttributeType()— returns theITypeDescriptorof the attribute struct.GetAttributeValueCount()— returns the number of constructor argument values.GetAttributeValue(index)— returns the boxedValueof 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:<AttributeTypeName>(<ArgTypeName>:<SerializedData>, ...)
@ParamAttribute:<ParameterName>:<AttributeTypeName>(<ArgTypeName>:<SerializedData>, ...)
Usage Example
Define attribute structs
struct MyAttribute
{
WString name;
vint number;
auto operator<=>(const MyAttribute&) const = default;
};
struct EmptyAttribute
{
auto operator<=>(const EmptyAttribute&) const = default;
};
Register attribute structs
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
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
#define _ ,
BEGIN_CLASS_MEMBER(MyClass)
ATTRIBUTE_TYPE(MyAttribute, L"type", 1)
ATTRIBUTE_TYPE(EmptyAttribute)
CLASS_MEMBER_CONSTRUCTOR(Ptr<MyClass>(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_REFLECTIONis not defined. - They are excluded when
VCZH_DEBUG_NO_REFLECTIONis defined (no reflection mode). - They work in both full reflection and metaonly reflection (
VCZH_DEBUG_METAONLY_REFLECTION) builds.
Error Handling
ATTRIBUTE_MEMBERraisesCHECK_ERRORif no member has been registered yet.ATTRIBUTE_PARAMETERraisesCHECK_ERRORif the last registered member is not a method or constructor.ATTRIBUTE_PARAMETERraisesCHECK_ERRORif the named parameter does not exist or is ambiguous.- A
static_assertfires at compile time ifTYPE{ ARG1, ... }is not a valid expression. CHECK_ERRORis raised if the attribute type is not a reflected struct or if an argument is not serializable.
Workflow Script Attributes
Workflow script has its own attribute syntax (@category:name) that builds on the C++ reflection attribute infrastructure documented above.
For naming conventions, predefined attributes, assembly population, and binary serialization details, see KB_Workflow_Design_AttributeSystem.md.