-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nesting FieldFlags Types #13
Comments
I definitely don't have the bandwidth for several months, so I wouldn't wait on a PR from me, but if you don't get around to this, I think I could get it working with the "register new types within FieldFlags" solution you suggest on Discourse. My naive thought would be a table that stores Is there other metadata besides those three that would be needed to construct the field? |
Just that unfortunately isn't enough - for example, when someone has a |
Sorry, I was ambiguous! The table I'm proposing would carry metadata only for types defined using FieldFlags. For all other So the check is something like:
If it isn't in the table, fall back to #9. I might be full of it here; I'm away from my computer, so I can't test this theory at the moment. But I just wanted to clarify that I wasn't suggesting a map of One question this raises is if a |
The issue is that this needs a map
Both modules define a (Un)Packing the object is the easy part in all of this; that's just a lookup of the object type and a conversion, similar to how
That's IMO an orthogonal question. This usecase is also already supported with |
To summarize, there are two parts to this:
The second part is likely solved by a traits-like approach (which I'd REALLY prefer to be a subtyping query, but alas, we don't have multiple abstract inheritance...). So something like
with per-type defined overrides to |
Here is a MWE of what I was describing above: module A
# Keep track of types we made
const Type_Table = Tuple{Type, Int64}[]
const Missing_Row = (Any, 0)
update_table(::Type{T}, width) where T = push!(Type_Table, (T, width))
# check if the name/module combo for this call already exists
function check_for_type(mod, name)
fail_response = (false, Missing_Row)
name in names(mod; all=true) || return fail_response
obj = getproperty(mod, name)
obj isa Type || return fail_response
matching_row_i = findfirst(row -> row[1] === obj, Type_Table)
matching_row_i === nothing && return fail_response
return (true, Type_Table[matching_row_i])
end
macro make_type(name, width)
check_result = check_for_type(__module__, name)
@show check_result
if check_result[1]
println("I already made $name in this module! It is $(check_result[2][2]) wide.")
return :()
end
esc_name = esc(name)
return quote
struct $esc_name end
update_table($esc_name, $width)
end
end
end
module B
import ..A
@A.make_type T1 5
@A.make_type T2 10
# Check catches we already defined this type
@A.make_type T1 6
end
module C
import ..A
import ..B
import ..B: T2
# no conflict because B.T1 !- C.T1
@A.make_type T1 5
# Fails because T2 was imported
@A.make_type T2 5
end Also, not sure if this helps in #9, but |
I didn't realize the "real" bit width was also stored in With that in mind, I think you're absolutely right that a traits-style solution to problem 2 in your response makes the most sense. The table is redundant if we don't need to store metadata. |
Yes - as long as the macro can infer all sizes at macro expansion time (which is required anyway - we don't currently have variable sized structs in julia, at least in any manner that's convenient to use & implement with little to no overhead), all of this offset/bitwidth business can be hardcoded per-type. That's also exactly how the package works right now and is why only literal sizes are supported. The true bitwidth isn't actually stored, but it can be trivially computed by calculating the difference between the last bit position of the last field and |
Ok, one thing I've noticed - something like nesting a mutable in anything is not going to work, because julia> Meta.@lower person.health.basic_needs.hungry = true
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ %1 = Base.getproperty(person, :health)
│ %2 = Base.getproperty(%1, :basic_needs)
│ %3 = true
│ Base.setproperty!(%2, :hungry, %3)
└── return %3
)))) and the result of that last There could be a way to do it, if Immutables can work fine though, since you need to replace them with a new instance explicitly anyway. I guess that means you'll need some other way to make your fields shared between objects? Maybe some macro emitting the shared fields of an object, so they can be accessed directly? I'd have to add support for looking into quoteblocks in |
Ah, yeah, huh! I'm glad you looked at that. I'll ponder and get back to you when I have a decent proposal. |
Sounds good! I'll leave this issue open though, since storing primitive types and immutable bitstypes that are padding free is possible. It's certainly lower on the priority list though, the usecases for that exact thing seem rather niche. |
It would be helpful if fields of a
@bitfields
struct could be type annotated with types generated byFlagFields
so that you could nest types for cleaner code organization without losing the tight packing.For example:
Could be considered once #9 is implemented.
The text was updated successfully, but these errors were encountered: