Macro to create a generated function

Hello.

I want to create a macro that reads:

macro store(agent, value)

    f_name = Symbol("store_", value)

    f = quote 
        @inline @generated function $f_name(a, v)
            return quote a.value = v end
        end
    end

    eval(f)
    return :($f_name($agent, $value))
    
end

The idea is that the macro creates a generated function(it needs to be generated as if the agent does not include that field then it returns :(nothing), I did not include that part) to store the value in the agent. Unfortunately, I cannot seem to get the line:

return quote a.value = v end

to work either value is there or the data that is stored in value at call(ie [100]). I think that is because it is a quote within a quote. Does anybody know how to achieve that?

Thank you in advance.

You don’t need a generated function, I think constant propagation handles this fine these days

julia> struct MyStruct
           a_field::Int
       end

julia> get_a_field(x) = hasfield(typeof(x), :a_field) ? x.a_field : nothing
get_a_field (generic function with 1 method)

julia> m = MyStruct(1)
MyStruct(1)

julia> @code_typed get_a_field(m)
CodeInfo(
1 ─ %1 = Base.getfield(x, :a_field)::Int64
└──      return %1
) => Int64

julia> struct MyOtherStruct
           b_field::Int
       end

julia> mo = MyOtherStruct(2)
MyOtherStruct(2)

julia> @code_typed get_a_field(mo)
CodeInfo(
1 ─     nothing::Nothing
└──     return Main.nothing
) => Nothing
1 Like

Very cool!

I did an experiment where I was checking hasfield() in a for loop and it was noticeably slower. Do you know what triggers constant propagation in this case?

Thank you for answering. You seem very knowledgeable about Julia would you happen also to know how to fix the above code, for future reference?

The symbol you are checking just needs to be fixed within a function (it can be fixed before and then fed in, it just has to be constant somewhere close enough for the compiler to use it), then the compiler can check the type for the field beforehand. If you pass it in dynamically as a Symbol argument, that doesn’t work. All places where you write obj.field in some code this is the same as writing getfield(obj, :field) so that kind of constant propagation / folding happens all the time.

1 Like