PropertyMethods
Suppose a type needs to define non-field properties. This might be because it needs to implement some of the properties of another type to whose interface it must conform. The Delegation pattern is an example. There could be other reasons why some properties need to be computed rather than directly read.
Here is an example, though somewhat contrived:
using PropertyMethods
mutable struct Rectangle
x
y
width
height
end
mutable struct OffsetRectangle
rect
x_offset
y_offset
end
Base.getproperty(r::OffsetRectangle, ::Val{:x}) =
r.x_offset + r.rect.x
Base.getproperty(r::OffsetRectangle, ::Val{:y}) =
r.y_offset + r.rect.y
Base.getproperty(r::OffsetRectangle, ::Val{:width}) =
r.rect.width
Base.getproperty(r::OffsetRectangle, ::Val{:height}) =
r.rect.height
@property_trampolines OffsetRectangle@property_trampolines is necessary to define some additional methods:
using MacroTools
MacroTools.striplines(@macroexpand @property_trampolines OffsetRectangle)quote
(PropertyMethods.Base).getproperty(var"#15#o"::OffsetRectangle, var"#16#prop"::PropertyMethods.Symbol) = begin
(PropertyMethods.Base).getproperty(var"#15#o", PropertyMethods.Val(var"#16#prop"))
end
((PropertyMethods.Base).getproperty(var"#17#o"::OffsetRectangle, var"#18#prop"::PropertyMethods.Val{var"#19#T"}) where var"#19#T") = begin
PropertyMethods.getfield(var"#17#o", var"#19#T")
end
(PropertyMethods.Base).propertynames(var"#20#o"::OffsetRectangle, var"#21#private"::PropertyMethods.Bool = false) = begin
PropertyMethods.propertynames_from_val_methods(PropertyMethods.typeof(var"#20#o"), var"#21#private")
end
(PropertyMethods.Base).hasproperty(var"#22#o"::OffsetRectangle, var"#23#prop"::PropertyMethods.Symbol) = begin
var"#23#prop" in PropertyMethods.propertynames_from_val_methods(PropertyMethods.typeof(var"#22#o"), true)
end
endrect1 = Rectangle(0, 1, 2, 4)
orect = OffsetRectangle(rect1, 2, 2)
for prop in propertynames(orect)
println(prop, '\t', getproperty(orect, prop))
endrect Main.Rectangle(0, 1, 2, 4)
x_offset 2
y_offset 2
height 4
width 2
y 3
x 2Some of the getproperty methods we defined just trampoline to the value of the OffsetRectangle's rect field. This is just boilerplate. We can do better:
MacroTools.striplines(@macroexpand @delegate Rectangle rect width height)quote
Base.getproperty(o::Rectangle, ::Val{:width}) = o.rect.width
Base.getproperty(o::Rectangle, ::Val{:height}) = o.rect.height
endSo, instead of explicitly coding the OffsetRectangle methods for the width and height fields, we can write
@delegate Rectangle rect width heightorect.width2Index
PropertyMethods.propertynames_from_val_methodsPropertyMethods.@delegatePropertyMethods.@property_trampolines
Definitions
PropertyMethods.propertynames_from_val_methods — Method
propertynames_from_val_methods(t::Type, private::Bool)Compute and return the propertynames for type t from its fields and Val specialized methods.
PropertyMethods.@delegate — Macro
@delegate from_type to_field properties...Defines getproperty methods on from_type that for each of the enumerated properties that will return the value of that property from a from_type instance's to_field.
PropertyMethods.@property_trampolines — Macro
@property_trampolines MyStructDefine the methods necessary so that Val specialized getproperty methods for MyStruct will find Val specialized properties.