Extracting all struct fields

ideally i would like to do this

struct MyStruct
   a
   b
   c
end

function calculation(C::MyStruct)
   fieldnames(MyStruct)... = C
   2a+3b+c
end

i was trying this workaround


struct My
    a
    b
    c
end


function __unpack_genexpr(S)
    a=quote
    end
    for x in fieldnames(typeof(S))
        a=quote
            $(a)
            $x=$S.$x
        end
    end
    a
end
macro unpack(S)
    :(eval($__unpack_genexpr($S)))
end

function bla()
    @unpack My(6,7,8)
    a,b,c
end

bla()   # (6,7,8)

it is ugly as hell and it leaks in the main module, what can i do?

2 Likes

it is not the same, i want to extract all fields automatically

1 Like

No, I’m afraid there’s no way to do this because there is no way to dynamically create local variables. See Unpack named tuples (does anyone know how to do it?) - #9 by rdeits and Is it possible to get the fieldnames of a struct inside a macro? - #15 by rdeits for some related discussion.

2 Likes

Maybe with a generated function?

struct MyStruct
   a
   b
   c
end

@generated function calculation(ms::MyStruct)
    assignments = Expr(:block, [
        :($field = ms.$field)
        for field in fieldnames(ms)
    ]...)
    quote
        $assignments
        2a+3b+c
    end
end
julia> calculation(MyStruct(1,2,3))
11

The above implementation has (at least) the caveat that the name used for the function argument (ms in this case) must not conflict with any of the field names.

But I know that writing generated functions is often frowned upon; please let me know if there is something I overlooked.

implementation of @open in ParametrisedModule can be what you want.

1 Like

You can use macro manipulation libraries(like MacroTools) to make it look pretty easy and short, and use esc properly to avoid scoping issues.

1 Like

One option is to go in a different way and interpolate the struct fields into the body. This can maybe serve as inspiration:

macro with(declaration, expr)
    return esc(with_func(declaration, expr))
end

function with_func(declaration, expr)
    @assert declaration.head == :(::)
    v, typ = declaration.args
    fields = fieldnames(getfield(@__MODULE__, typ))
    expr = recursive_interpolation(expr, fields, v)
    return expr
end

function recursive_interpolation(expr::Expr, fields, v)
    return Expr(expr.head, recursive_interpolation.(expr.args, Ref(fields), v)...)
end

function recursive_interpolation(s::Symbol, fields, v)
    if s in fields
        return :($(v).$(s))
    end
    return s
end

recursive_interpolation(expr, fields, v) = expr

struct My
    a
    b
end

a = My(2, 3)

function f(x)
    @with x::My begin
        return a + 2b
    end
end

f(a)

Disclaimer: I’m not entirely sure that @__MODULE__ does the right thing in all circumstances.