I don’t want to write lots of code and use @eval now. I want to generate different code depending on a condition inside @eval. It looks like this:
for (sym, len) in (
(:sym1, nothing),
(:sym2, 2)
)
@eval struct $sym
value::String
function $sym(v::String)
$( # This block doesn't work
if len === nothing
nothing
else
length(v) == len || throw(DomainError(v, "")) # Here the compiler says that it doesn't know `v`
end
)
new(v)
end
end
@eval Base.getindex(object::$sym) = object.value
@eval Base.length(object::$sym) = length(object[])
end
Is there a way to execute code inside the expression I create for the @eval macro?
@eval struct $sym
value::String
function $sym(v::String)
$(
if len === nothing
nothing
else
:(length(v) == $len || throw(DomainError(v, "")))
end
)
new(v)
end
Are you sure this is the best way to handle your use case though? Obviously you are the best judge of that, and perhaps your real use is more complex, but for something like this I’d probably use a parametric type instead of defining types in a for loop
edit: looks like you can also simplify to
@eval struct $sym
value::String
function $sym(v::String)
if $len !== nothing
length(v) == $len || throw(DomainError(v, ""))
end
new(v)
end
In both cases, the check is done at compile time. The compiler does constant propagation and code elimination. For example, the following will compile to a no-op:
julia> function f()
if 1+1 == 3
error("Huh?")
end
end
f (generic function with 1 method)
julia> @code_llvm f()
; @ REPL[1]:1 within `f'
define void @julia_f_206() {
top:
; @ REPL[1]:3 within `f'
ret void
}
(The test len === nothing, does not even depend on constant propagation. The compiler can figure it out from just the type information.)
To clarify what I meant, it would be something like
struct Sym{N}
value::String
function Sym{N}(v) where N
N === nothing || @assert length(v) == N
new{N}(v)
end
end
# you can even make some aliases
for (sym, len) in ((:sym1, nothing), (:sym2, 2))
@eval const $sym = Sym{$len}
end
Works the same way while also being more general, and requiring no metaprogramming.
julia> sym1("aaaa")
sym1("aaaa")
julia> sym2("aaaa")
ERROR: AssertionError: length(v) == N
Stacktrace:
[1] sym2(v::String)
@ Main ./REPL[16]:4
[2] top-level scope
@ REPL[24]:1
julia> sym2("aa")
sym2("aa")
julia> Sym{4}("aaaa") # can alias this one too if you want.
Sym{4}("aaaa")