Open tamasgal opened 4 years ago
created dynamically (later it will define it from the streamer info)
I have time and started looking at the code base again. I'm wondering if it's possible to document an implementation-independent way in a Developers / Internals
section of the docs so people can understand how conceptually it works.
Particularly, I am yet to understand how to make a struct
for a streamer that we have never seen before, my guess is that the name
of streamer and byte
# for each field
are encoded in TKey
(?) or somewhere, but if that is the case does this mean we don't have to do any bootstrap except for the TKey
(which is ~fixed) and the starting 37(or 75) bytes of the .root
files?
Oh dear, I somehow forgot to answer this. I thought I did 🤔
Anyways, the construction of the struct and also the complete parsing of the structures is very complex. There is a Google group where I also participate in and we were already thinking about writing a book or so about the ROOT I/O to consolidate our knowledge. But I don't know about the time-scale, probably not this year...
So indeed, it's hard to answer your question as it's also depending on other fields in TKey
, which have magic values, for example listed here: https://github.com/tamasgal/UnROOT.jl/blob/1d892e7b9159e5425ea91a6cc16dabc1caa09b77/src/constants.jl#L27
Worth to mention, a Macro posted by Chris Rackauckas on twitter:
macro def(name, definition)
return quote
macro $(esc(name))()
esc($(Expr(:quote, definition)))
end
end
end
@def myfields
x::Int
y::Int
end
struct A
@myfields
z::Int
end
With this macro, anyone can compile-time paste code around.
The current design of the streamer type hierarchy (including versioning) works in a way which I do not find nice at all. At some point I thought it would be a nice solution but now I see a lot of code repetitions.
A lot of streamer logic is currently implemented in
bootstrap.jl
and the overall implementation of a streamer and all of its versions is done like this:As seen above, the
ROOTStreamedObject
is the supertype (which is just a simple abstract type) and there is (always) one single subtype (another abstract type) representing a specific streamer.Different versions are defined by appending
_VERSION
to the type name and creating an empty struct. Thereadfields!
method then define how to read its fields.Both versions of
TAttLine
(v1 and v2) have the same fields with the same types, so in this case it's just code repetition.Note that this definitions should be created dynamically in future, but the overall layout is of course the same.
During the read-in of a ROOT file, UnROOT checks if there is a streamer with the given name and version (appended), like
TAttLine_2
and if not, it will complain. (later it will define it from the streamer info).A simplification of this system which allows a nice bootstrapping (no code repetition) and a better version management would be something like the following:
where the actual struct definition could of course be simplified via a macro like
@rootstreamer TAttLine
or so.The
readfields!()
methods can then be defined using value type dispatch:Here is a short demo of the instantiation:
This is just an idea, any feedback is highly appreciated ;)