# Hello, i have a struct and i want to get with a macro a class builder and
# constructor Foo, like this:
# i have
struct Foo
a::Int64
b::Int64
c::Any
d::String
e::Union{Nothing,Foo}
# ...
function Foo(; a,b,c,d,e)
return new(a,b,c,d,e)
end
end
# i need
mutable struct FooBuilder
a::Union{Nothing,Int64}
b::Union{Nothing,Int64}
c::Any
d::Union{Nothing,String}
e::Union{Nothing,Foo}
end
function Foo(builder::FooBuilder)
return Foo(
a = builder.a,
b = builder.b,
c = builder.c,
d = builder.d,
e = builder.e,
)
end
# i do
macro builder(
type::Symbol,
builder::Symbol = Symbol(type, :Builder),
)
if @eval !isstructtype($type)
error("`$type` is not struct type")
end
if @eval @isdefined($builder)
error("`$builder` is defined")
end
wrap(T::Type) = nothing isa T ? T : Union{Nothing,T}
unite(name::Symbol, T::Type) = :($name::$T)
unite(value::Symbol, key::Symbol) = :($key=builder.$value)
local T = @eval $type
local fields = unite.(
T |> fieldnames,
T |> fieldtypes .|> wrap,
)
local params = unite.(
T |> fieldnames,
T |> fieldnames,
)
return quote
mutable struct $builder
$(fields...)
end
function $type(builder::$builder)
return $type(
$(params...)
)
end
end
end
@macroexpand @builder Foo
# i get
begin
mutable struct FooBuilder
a::Union{Nothing, Int64}
b::Union{Nothing, Int64}
c::Any
d::Union{Nothing, String}
e::Union{Nothing, Foo}
end
function var"#30#Foo"(var"#36#builder"::Main.FooBuilder)
return var"#30#Foo"(
$(Expr(:(=), Symbol("#31#a"), :((var"#36#builder").a))),
$(Expr(:(=), Symbol("#32#b"), :((var"#36#builder").b))),
$(Expr(:(=), Symbol("#33#c"), :((var"#36#builder").c))),
$(Expr(:(=), Symbol("#34#d"), :((var"#36#builder").d))),
$(Expr(:(=), Symbol("#35#e"), :((var"#36#builder").e)))
)
end
end
# what am I doing wrong ?
# why i get var"#30#Foo" or #31#a instead of Foo or :a ?
# where is my function Foo(builder::Main.FooBuilder) ?
Perhaps getname($type)
instead of $type
where you build the function?
getname ?
This is about macro hygiene. e.g. here is a simple example:
julia> macro foo(ex)
ex
end
@foo (macro with 1 method)
julia> @macroexpand @foo x = 1
:(var"#64#x" = 1)
julia> macro foo(ex)
esc(ex)
end
@foo (macro with 1 method)
julia> @macroexpand @foo x = 1
:(x = 1)
Here is the relevant docs section on Hygiene: Metaprogramming · The Julia Language
2 Likes
спасибо