I had some time this morning to try my hand at a macro-based solution.
This is indeed impressive macro work!
I used
mutable struct Part
a::Float32
end
@with_inline_types mutable struct Whole
b::Int32
part::@inline(Part)
end
w = Whole(Int32(42), Part(42.0f0))
to stay compatible with the rest of the discussion (but great that the solution is tested with nested structs)
Note that I have only a limited understanding of the macro implementation, so please correct points I haven’t evaluated correctly, so I can correct the list.
These are the pros and cons I see :
pros:
- clearly defined constructor semantics
- seems to have ideal memory behavior (only one alloc, clear GC story)
- field access works with nested structs
- preserves user defined
getproperty
forWhole
:
Base.getproperty(w::Whole, s::Symbol) = s === :c ? true : getfield(w, s)
julia> w.c
true
- Preserves object identity (although I do not understand why)
w1 = Whole(Int32(1), Part(1.0))
w2 = Whole(Int32(1), Part(1.0))
julia> w1.part === w1.part !== w2.part === w2.part
true
- methods involving
Part
can be called (using aUnion
or subtyping):
cons:
- constructor no longer supports type conversion (
Whole(42, Part(42.0)
does not work) - definition syntax more complicated as macro needed both before struct definition and inside field definition
- returns “wrong” fieldnames:
julia> Whole |> fieldnames
(:b, :part__a)
- returns “wrong” type for substructs:
w.part |> typeof
julia> w.part
PartView{:part, Whole}(Whole(42, 42.0f0))
Part
’s methods needs to accept aUnion
or an abstract type (thenPart
needs to use that, too) to work- Does not support getting the memory address
julia> w1.part |> pointer_from_objref
ERROR: pointer_from_objref cannot be used on immutable objects
- Returns different types for multiple fields of the same type
So while it does work for the simplest of cases, it seems to be too limited (but I might be using it incorrectly).
Edit: corrections due to additional input (see below)
Edit 2: added foobar_lv2
’s limitation