ForwardMethods.array_interface
— MethodForwardMethods.array_interface(T; index_style_linear::Bool, omit=Symbol[])
Forwards the following methods for x::T
:
Base.size(x::T)
Base.iterate(x::T)
Base.iterate(x::T, state)
Base.length(x::T)
Base.IndexStyle(::Type{T})
Base.getindex(x::T, index)
Base.setindex!(x::T, value, index)
The signatures for Base.getindex
+ Base.setindex!
will be set according to the value of index_style_linear
Any function names specified in omit::AbstractVector{Symbol}
will not be defined
ForwardMethods.dict_interface
— MethodForwardMethods.dict_interface(T; omit=Symbol[])
Forwards the following methods for x::T
:
Base.keys(x::T)
Base.values(x::T)
Base.pairs(x::T)
Base.length(x::T)
Base.isempty(x::T)
Base.empty!(x::T)
Base.iterate(x::T)
Base.iterate(x::T, state)
Base.pop!(x::T, key)
Base.delete!(x::T, key)
Base.haskey(x::T, key)
Base.getindex(x::T, key)
Base.setindex!(x::T, value, key)
Base.get(x::T, key, default_value)
Base.get!(default::Callable, x::T, key)
Base.in(value, x::T)
Any function names specified in omit::AbstractVector{Symbol}
will not be defined
ForwardMethods.equality_interface
— Methodequality_interface(T; [omit=Symbol[], equality_op=:(==), compare_fields=:fieldnames])
Defines the equality_op
operator for two objects of type T
in the natural way, i.e.,
Base.:(==)(x::T, y::T) = all( getfield(x,k) == getfield(y,k) for k in fieldnames(T))
Arguments
If equality_op == isequal
, defines Base.isequal
instead.
If compare_fields == propertynames
, the above definition uses getproperty
and propertynames(x)
instead of getfield
and fieldnames(T)
, respectively.
Any values provided in omit
are excluded from the generator expression above.
ForwardMethods.getfields_interface
— Methodgetfields_interface(T; omit=Symbol[])
Given x::T
, forwards the method $field(x::T)
to getfield(x, $field)
, for each field in fieldnames(T)
ForwardMethods.indexing_interface
— MethodForwardMethods.indexing_interface(T; omit=Symbol[])
Forwards the following methods for x::T
:
Base.getindex(x::T, index)
Base.setindex!(x::T, value, index)
Base.firstindex(x::T)
Base.lastindex(x::T)
Any function names specified in omit::AbstractVector{Symbol}
will not be defined
ForwardMethods.iteration_interface
— MethodForwardMethods.iteration_interface(T; omit=Symbol[])
Forwards the following methods for x::T
:
Base.iterate(x::T)
Base.iterate(x::T, state)
Base.IteratorSize(::Type{T})
Base.IteratorEltype(::Type{T})
Base.eltype(::Type{T})
Base.length(x::T)
Base.size(x::T)
Base.isdone(x::T)
Base.isdone(x::T, state)
Any function names specified in omit::AbstractVector{Symbol}
will not be defined
ForwardMethods.lockable_interface
— MethodForwardMethods.lockable_interface(T; omit=Symbol[])
Forwards the following methods for x::T
:
Base.lock(x::T)
Base.lock(f, x::T)
Base.unlock(x::T)
Base.trylock(x::T)
Base.islocked(x::T)
Any function names specified in omit::AbstractVector{Symbol}
will not be defined
ForwardMethods.properties_interface
— Methodproperties_interface(T; delegated_fields, [recursive::Bool=false], [ensure_unique::Bool=true])
Provides amalgamated property-based access to x::T
in terms of its fields as well as the fields of its children. So that, i.e., the fact that x
is composed of fields k1::T1, k2::T2, ..., kn::Tn
becomes an implementation detail of the construction of x
.
More specifically, given the set of symbols delegated_fields
and x::T
for a struct type T
, defines:
Base.propertynames(x::T)
in terms offieldnames(T)
, as well as thefieldnames
ofgetfield(x, k)
for eachk
indelegated_fields
.Base.getproperty(x::T, name::Symbol)
to returngetfield(getfield(x, k), name)
for the firstk ∈ delegated_fields
such thathasfield(getfield(x, k), name)
. If no suchk
exists, defaults to returninggetfield(x, name)
.- and analogously for
Base.setproperty!(x::T, name::Symbol, value)
If recursive == true
, instances of getfield/hasfield/setfield
in the above are replaced by internal methods that default to Base.getproperty/Base.setproperty/Base.hasproperty
, respectively.
If ensure_unique == true
, throws an error when there are nonunique names in the amalgamation of the fields of x
with the fields/recursive properties of each field in delegated_fields
. If this option is false
, any of the above setting/getting operations above will use the first (possibly recursive) child field found matching the above criteria.
Arguments
delegated_fields
can be either a Symbol
, or a vect
or tuple
Expr
of Symbol
s, corresponding to fieldnames of T
ForwardMethods.setfields_interface
— Methodsetfields_interface(T; omit=Symbol[])
Given x::T
, forwards the method $field!(x::T, value)
to setfield!(x, $field, value)
, for each field in fieldnames(T)
ForwardMethods.vector_interface
— MethodForwardMethods.vector_interface(T; omit=Symbol[])
Forwards the methods for x::T
from ForwardMethods.array_interface(T; index_style_linear=true)
, ForwardMethods.iteration_interface(T)
, and ForwardMethods.indexing_interface(T)
Any function names specified in omit::AbstractVector{Symbol}
will not be defined.
ForwardMethods.@define_interface
— Macro@define_interface T [interface=name] [kwargs...]
Defines the interface
for objects of type T
Arguments
name
must be one of (:properties, :equality, :setfields, :getfields), with name
value f
corresponding to the interface definition function $f_interface
(e.g., array
=> array_interface
).
The key=value
pairs will be forwarded to the corresponding interface definition method. In particular, specifying omit=func1
or omit=[func1,func2, ..., funcn]
will omit func1
, ..., funcn
from being forwarded by this macro.
Refer to the documentation of each name_interface for the specific keyword arguments required, if any.
ForwardMethods.@forward_interface
— Macro@forward_interface T [field=_field] [interface=name] [map=_map] [key=value...]
Forwards the methods defined for interface
to objects of type T
Arguments
name
must be one of (:iteration, :indexing, :array, :vector, :dict, :lockable, :getfields, :setfields), with name
value f
corresponding to the interface definition function $f_interface
(e.g., array
=> array_interface
).
If name
is either getfields
or setfields
, then the field
keyword argument is ignored
Otherwise, _field
must be one of the following expressions
k::Symbol
ork::QuoteNode
, or an expression of the formgetfield(_, k)
, in which case methods will be forwarded togetfield(x, k)
- an expression of the form
getproperty(_, k)
, in which case methods will be forwarded togetproperty(x, k)
- an expression of the form
t[args...]
, in which case methods will be forwarded tox[args...]
- or an expression of the form
f(_)
for a single-argument functionf
, in which case methods will be forwarded tof(x)
Additional Arguments
The key=value
pairs will be forwarded to the corresponding interface definition method. In particular, specifying omit=func1
or omit=[func1,func2, ..., funcn]
will omit func1
, ..., funcn
from being forwarded by this macro.
See also @forward_methods
ForwardMethods.@forward_methods
— Macro@forward_methods T [field=_field] [map=_map] methods...
Forwards the method definitions for x::T
, depending on the value of _field
.
_field
must be one of the following expressions
k::Symbol
ork::QuoteNode
, or an expression of the formgetfield(_, k)
, in which case methods will be forwarded togetfield(x, k)
- an expression of the form
a.b.c. ...
, in which case methods will be forwarded togetfield(getfield(getfield(x, :a), :b), :c) ...
- an expression of the form
getproperty(_, k)
, in which case methods will be forwarded togetproperty(x, k)
- an expression of the form
t[args...]
, in which case methods will be forwarded tox[args...]
- or an expression of the form
f(_)
for a single-argument functionf
, in which case methods will be forwarded tof(x)
For notational purposes below, call the forwarded value forwarded_value(x)
.
Each method
must be one of the following expressions
an expression of the form
f::Symbol
orA.f
, which will forward the single-argument functionf(x)
orA.f(x)
tof(forwarded_value(x))
orA.f(forwarded_value(x))
, respectivelya function signature of the form
f(args...)
, orf(args...; kwargs...)
. In this form, there must be either exactly one expression of the formx::T
or_
(an underscore) inargs
. If this occurs at positioni
, say, this macro will forward this method to the definitionf(args[1], args[2], ..., args[i-1], forwarded_value(x), args[i+1], ..., args[end]; kwargs...)
When
field
maps to invokinggetfield
for fieldk
, one can also write an argument expression of the form::T
orx::T
. In this case, this macro definition will definef(args[1], ..., args[i-1], ::Type{T}, args[i+1], ..., args[end]; kwargs) = f(args[1], ..., args[i-1], fieldtype(T, k), args[i+1], ..., args[end]; kwargs)
which can be useful for forwarding, for instance, trait-based methods to the corresponding container type.
Each method
may also be a :block
expression, where each sub-expression satisfies one of the above conditions
Optional Arguments
The optional map=_map
parameter allows you to apply an expression transformation to the resulting forwarded expression. _map
must be an expression containing at least one underscore _
placeholder and optionally _obj
placeholders. _
and _obj
will be replaced with the transformed function and initial object argument, respectively.
For example, if we have
struct LockableDict{K,V}
d::Dict{K,V}
lock::ReentrantLock
end
@forward LockableDict{K,V} field=lock Base.lock Base.unlock
@forward LockableDict{K,V} field=d map=(begin lock(_obj); try _ finally unlock(_obj) end end) Base.getindex(_, k)
Then the expressions generated in the second statement are (roughly) of the form
function Base.getindex(l::LockableDict{K,V}, k)
lock(l)
try
Base.getindex(getfield(l, :d))
finally
unlock(l)
end
end
Notes
- Parametric expressions are supported for
T
, as well as for the function signature form ofmethod
Examples
julia> struct B{T}
v::Vector{T}
end
julia> @forward_methods B{T} field=v Base.firstindex Base.lastindex Base.getindex(_, k::Int) Base.setindex!(x::B, v, k) (Base.eachindex(x::B{T}) where {T})
julia> b = B([1,2,3])
B{Int64}([1, 2, 3])
julia> for i in eachindex(b)
b[i] = b[i]^2
end
julia> b[end]
9
julia> struct C
d::Dict{String,Int}
end
julia> @forward_methods C field=d Base.keytype(::Type{C}) Base.valtype(::Type{C})
julia> keytype(C)
String
julia> valtype(C)
Int