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"#13#o"::OffsetRectangle, var"#14#prop"::PropertyMethods.Symbol) = begin
(PropertyMethods.Base).getproperty(var"#13#o", PropertyMethods.Val(var"#14#prop"))
end
((PropertyMethods.Base).getproperty(var"#15#o"::OffsetRectangle, var"#16#prop"::PropertyMethods.Val{var"#17#T"}) where var"#17#T") = begin
PropertyMethods.getfield(var"#15#o", var"#17#T")
end
(PropertyMethods.Base).propertynames(var"#18#o"::OffsetRectangle, var"#19#private"::PropertyMethods.Bool = false) = begin
PropertyMethods.propertynames_from_val_methods(PropertyMethods.typeof(var"#18#o"), var"#19#private")
end
(PropertyMethods.Base).hasproperty(var"#20#o"::OffsetRectangle, var"#21#prop"::PropertyMethods.Symbol) = begin
var"#21#prop" in PropertyMethods.propertynames_from_val_methods(PropertyMethods.typeof(var"#20#o"), true)
end
end
rect1 = Rectangle(0, 1, 2, 4)
orect = OffsetRectangle(rect1, 2, 2)
for prop in propertynames(orect)
println(prop, '\t', getproperty(orect, prop))
end
rect Main.Rectangle(0, 1, 2, 4)
x_offset 2
y_offset 2
height 4
width 2
y 3
x 2
Some 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
end
So, instead of explicitly coding the OffsetRectangle methods for the width
and height
fields, we can write
@delegate Rectangle rect width height
orect.width
2
Index
PropertyMethods.propertynames_from_val_methods
PropertyMethods.@delegate
PropertyMethods.@property_trampolines
Definitions
PropertyMethods.propertynames_from_val_methods
— Methodpropertynames_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 MyStruct
Define the methods necessary so that Val
specialized getproperty
methods for MyStruct
will find Val
specialized properties.