You can use primitive
types.
# define a type that acts like a "packed" 24-bit struct of x::UInt8, y::UInt16:
primitive type Foo 24 end
function Base.getproperty(foo::Foo, s::Symbol)
# create a Ref so that we can get a pointer to the raw bytes;
# I'm not sure if there is a nicer way (unless the primitive type has
# the right size to be be reinterpreted as a UInt64 or similar)
r = Ref(foo)
GC.@preserve r begin
if s === :x
return unsafe_load(Ptr{UInt8}(Base.unsafe_convert(Ptr{Cvoid}, r)))
elseif s === :y
return unsafe_load(Ptr{UInt16}(Base.unsafe_convert(Ptr{Cvoid}, r)+1))
end
end
error("unknown field $s")
end
Base.show(io::IO, foo::Foo) = print(io, "Foo(", foo.x, ',', foo.y, ')')
after which you can do:
julia> foodata = reinterpret(Foo, [0x01, 0x00, 0x10, 0x02, 0x00, 0x20])
2-element reinterpret(Foo, ::Array{UInt8,1}):
Foo(1,4096)
Foo(2,8192)
julia> foodata[2].y
0x2000