Selectively unpacking binary data

I need to unpack a few values of known types and offsets from a binary file. The difference with earlier discussed cases is, firstly, that the values are not adjacent in the file, so it is necessary to specify offsefs; secondly, there are some differences in the way the values are packed. So I may do something like this:

struct Foo
  val1:: Int32
  val2::Float16
  val3::UInt64
end
foo_layout=[12 :pack_style_1;  34 :pack_style_2; 56 pack_style_3]

Then the unpacking function itrates over fields of Foo, reads them into v::Vector{Any} , taking into account the offsets and styles, and then returns Foo(v...). The drawback of this solution is the necessity to keep in sync the separate definitions of Foo and its layout. It would be more convenient to supply some “pragmas” within the definion of Foo, and then having a macro to generate both the struct and the layout description:

@gen_layout struct Foo
  val1:: Int32 # 12 :pack_style_1
  val2::Float16 # 34 :pack_style_2
  val3::UInt64 # 56 :pack_style_3
end

What is unclear to me is, if the metaprogramming facilities work on the parsed AST, do they have access to the comments?

Or maybe, there’s some altogether better way to do this?

Somewhat better way to define the layout would be with traits:

struct Foo
  val1:: Int32
  val2::Float16
  val3::UInt64
end
layout(::Type{Foo})=[12 :pack_style_1;  34 :pack_style_2; 56 pack_style_3]

for T in [Foo, Bar, Baz]
    @assert fieldcount(T) == size(layout(T), 1)
end

In that case, it becomes unnecessary to export the layout definition and parsing function may look like this

function parse_struct(io::IOStream, ::Type{T})
  t_layout = layout(T)
  K = fieldcount(T)
  vars = Vector{Any}(undef, K)
  for k in 1:K
    seek(io, t_layout[k, 1])
    vars[k] = read_value(io, fieldtype(T, k), t_layout[k, 2])
  end

  return T(vars...)
end