Access to struct fields in generated function

Hi,

I am trying to wrap my skull around metaprogramming. Feels like a stiff skull.

In my little example, I want a function that can take any composite type, and do some manipulation (here printing) of each field. Here we go

using StaticArrays
@generated function insight(f)
    print("Generating insight.\n")
    e = quote end  
    for fi in fieldnames(f) 
        blk = quote 
            print(string(fi),"::",string(typeof(f.$fi))," → ",show(f.$fi),"\n")
        end
        append!(e.args, blk.args)
    end
    return e
end

struct typo{nx}
    σ::SMatrix{nx,nx}
    b
    c
end

nx = 3

i = typo{nx}(randn(nx,nx),π,3.)
insight(i)

I get “undefVarError: fi not defined.”
I know a “generated function” has access to type info, not values, at … generation time. I reckon fieldnames should be fair game. Are the fieldnames known at generation time? Is that the cause of the error anyway?

I also welcome comments on the way I append stuff. Is there simpler?

Philippe

You need to think about what computation happens when:

print(string(fi),
      "::",string(typeof(f.$fi))," → ",
      show(f.$fi),"\n")

The first string(fi) is quoted, so it will be evaluated when the (generated) function is called. fi is not a variable in your generated function, so it’s an error. You probably want $(string(fi)) to evaluate it at generation-time.

The second string should work fine. But it will be performing the computation at runtime, since only $fi will be evaluated at generation time. You could use $(string(fieldtype(f, fi))) to do it at compile-time (though note that it’s not quite the same computation — your version shows the type of the value stored, but my version shows the type of the field)

The third string is fine.

Thank you, that helps! One way to put it is:

@generate function foo(data)
generation time code
return quote 
    runtime code $(generation time code)
end

My code now works

using StaticArrays
@generated function insight(f)
    print("Generating insight.\n")
    e = quote end  
    for fi in fieldnames(f) 
        blk = quote 
            print($(string(fi))," :: ",$(string(fieldtype(f,fi)))," → ",string(f.$fi),"\n")
        end
        append!(e.args, blk.args)
    end
    return e
end

Just a minor correction:

@generated function foo(data)
    runtime code
    return quote 
        runtime code $(generation time code)
    end
end

should instead be

@generated function foo(data)
    generation time code
    return quote 
        runtime code $(generation time code)
    end
end

Indeed! I corrected my post.