API
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::Symbolork::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::SymbolorA.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::Tor_(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
fieldmaps to invokinggetfieldfor fieldk, one can also write an argument expression of the form::Torx::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
endNotes
- 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)
IntForwardMethods.@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::Symbolork::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.@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.iteration_interface — FunctionForwardMethods.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.indexing_interface — FunctionForwardMethods.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.lockable_interface — FunctionForwardMethods.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.array_interface — FunctionForwardMethods.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.vector_interface — FunctionForwardMethods.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.dict_interface — FunctionForwardMethods.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.getfields_interface — Functiongetfields_interface(T; omit=Symbol[])Given x::T, forwards the method $field(x::T) to getfield(x, $field), for each field in fieldnames(T)
ForwardMethods.setfields_interface — Functionsetfields_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.properties_interface — Functionproperties_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 thefieldnamesofgetfield(x, k)for eachkindelegated_fields.Base.getproperty(x::T, name::Symbol)to returngetfield(getfield(x, k), name)for the firstk ∈ delegated_fieldssuch thathasfield(getfield(x, k), name). If no suchkexists, 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 Symbols, corresponding to fieldnames of T
ForwardMethods.equality_interface — Functionequality_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.