-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
@export_flags integer size concerns #89916
Comments
I'd say this makes more sense as a proposal, as there's no bug here but a limitation that could be discussed |
godot/editor/editor_properties.cpp Lines 749 to 750 in 7d151c8
|
Thank you for the reference! But I am still not sure about why in the editor_properties.cpp If it doesn't take you too much time, would you mind bothering explaining a little more details? It'd be really appreciated. |
I'm not sure about the reasons, but my guess is:
|
Yeah, I totally agree with point 3. I was just trying to figure out something that doesn't quite make sense to me, but it shouldn't have a problem because in most scenarios nobody would go over that limit. Thank you so much for take the time to help and explain! I also have a little more questions about bitfields, if you don't mind taking a look at them as well. In gdscript, I didn't quite find some helpful bit operations/helper functions like popcount in C#/C++, or just simply toggle, set, clear, check (all/any) bits or group of bits, which make it easier and handy for developers to manipulate and utilize the flags in their game logics. Currently, in order to use exported checkboxes in inspector, it has to be an Another inconvenience is the flag types are only defined in the string arguments of I've been seeing many proposals (#74418 , #71234 (comment), godotengine/godot-proposals#923) regarding improving the usability of the bit fields. I am wondering if this area is open for contribution now, or do you prefer it to maintain its current behavior? If I make a pull request, would it be likely to be accepted? (Haven't contributed to godot yet, and I saw the best practices instruction says "before attempting to contribute, it is important to discuss the actual problems with the other developers or contributors") I am planning to add:
Does it sound okay to you? Thank you so much again for your time and patience! |
This is a fairly cosmetic change, it just changes "enum" to "flags" in the documentation. We could add validation for values of enum marked with
I was thinking that we could add a The problem is that we can't add methods like But even though there are no But I think we need some refactoring of GDScript's static analysis before adding major new features (adding a new
Personally, I don't like the idea of annotation overloads, even though they are resolved at compile time so they don't cause dispatch problems at runtime, unlike method overloads do. But we could add spread syntax instead, see below. Also, we could add bitfields support for the bare @export var a: MyEnum
@export var b: BitField[MyEnum]
An alternative approach is to add spread syntax, see godotengine/godot-proposals#8439. Annotations support constant expressions. You could declare a constant array and reuse it like this: const FLAGS = ["A", "B", "C"]
@export_flags(...FLAGS) var x: int
@export_flags(...FLAGS) var y: int I started working on the second part of #82808 and it's already partially done in my local branch, but I've paused it for now. |
Hi Danil! Sorry for the late response, and thank you for providing those helpful information and sharing your thoughts!
I think what I was suggesting is a little more than just turn the displayed type name into # Without annotations, the original default values are
enum PlainEnum{
A = 0,
B = 1,
C = 2,
D = 3
}
# But with the annotation, the default values would automatically be generated as powers of 2
@bitflags
enum AnnotatedEnum{
A = 1 << 0, # which is 1 or 0b0001
B = 1 << 1, # which is 2 or 0b0010
C = 1 << 2, # which is 4 or 0b0100
D = 1 << 3 # which is 8 or 0b1000
} And just by the way, I hope to discuss a little more about the naming: Although I understand that these terms may often be interchangeably used by programmers in development due to their similar expressions (for instance, they all represent integer data), after reviewing relevant materials, I've confirmed that they typically represent the following concepts with some subtle differences. I believe ensuring that we use the correct terminology when designing keywords is quite important, so the future users won't feel confused or mixed up:
To give a more intuitive sense of those terms, see the example below (This is written in syntax supported in current GDScript): class_name Spell
# These const ints are bit flags
enum ATTACK_TYPE{
FIRE = 1, # 0b0001
WATER = 2, # 0b0010
EARTH = 4, # 0b0100
WIND = 8 # 0b1000
}
# This member property is bit field
@export_flags("Fire", "Water", "Earth", "Wind") var spell_elements := 0:
# Suppose you want to add some constraints to the bitfield by setter.
set(value):
# Checks if Fire and Water is set simultaneously,
# so 0b0011 is bit mask
var bitmask := ATTACK_TYPE.FIRE | ATTACK_TYPE.WATER # 0b0011
if bitmask & value == bitmask:
push_warning("Incompatible elements coexist: Fire and Water")
else:
spell_elements = value Therefore, if we add a new annotation to
It sounds like a good idea to me. Can you explain what the difficulties are? If we introduce a new
Or do we add those methods to
I think I have been watching and looking forward to a spread syntax thing so we could finally have variadic functions. However, I am not sure if the spread syntax here (in the exported bitfield context) is a good idea:
Regarding point 2, consider this example: const FLAGS = ["FIRE", "WATER", "EARTH", "WIND"]
@export_flags(…FLAGS) var spell_elements: int = 0 Then, when you want to utilize the flags, checking, toggling, setting, or clearing any combination of the flags, you either need to:
Sounds cool! Thank you for implementing this - But what stopped you? So to wrap up here, over the discussions and potential solutions I've been seeing and pondering on this topic, I personally & currently think it would be the best, to:
I hope my statements make some sense. Let me know! Thank you. |
I'm not strictly against this, but I have some concerns:
I understand your concerns and partly agree with them. But, sorry, I won't comment on this in detail to save time. We can discuss naming issues if/when we start implementing the feature. Just to note that the proposed terminology is probably borrowed from the core (
Any value in GDScript is a We have
It's possible, here's a proof of concept ( static void func_int_incremented(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
int64_t *n = VariantGetInternalPtr<int64_t>::get_ptr(v);
r_ret = *n + 1;
}
bind_custom(int, incremented, _VariantCall::func_int_incremented, true, int); But the problem I see is that methods of types that are passed by value should be immutable, like in
GDScript is not based on Python and is not required to follow its syntax. The asterisk is more like pointers, the ellipsis is more obvious, so I think consistency with C++, JavaScript, PHP, and Godot documentation makes more sense here than with Python. See godotengine/godot-proposals#1034 (comment) and and further discussion.
Yes, the main benefit is that you don't have to duplicate it when declaring multiple variables (like we have to in the core). As I said above, I think
I'm postponing this due to higher priority issues and because this is quite a complex task in terms of static analysis (plus I have a few refactoring ideas that would simplify the task in the future). |
Yeah I also saw this before in the documentation. Why not we just keep the behavior we currently have for
I did just want to add it as syntactic sugar. I haven't quite thought much about the guarantees or hints implied by enumerations, and I am not sure if I fully understand what you mean. Can you explain a little more details like what kind of runtime guarantees a user might expect? Will, in this case, adding notes to related documentation help them feel less confused?
Yes you are right, it is not too bad. Then the code is loaded down with many unnecessary hard-coded values that could have been handled and inferred more intelligently by the interpreter. What's even worse, for example, when you have to manually assign those flag values, and you then just find out you need to insert or delete some values from the middle, it will be a pain to manually re-order all the values after it. Same thing would happen when you want to merge or split the elements of enums. When there're needs for modification, such as redesigning, refactoring, reordering, or reusing some parts, it becomes a nightmare. Not to mention that for now you need to maintain both the enum and the string arguments list of This painful process happened over and over again during my development, so I am currently using some tool scripts to manage them, but it's still somewhat troublesome. I think it would be much better for the GDScript language itself to provide a builtin solution so other users will have a better native experience without needing to write their own tools. By the way, how could implicit values also have typo problems? I think I didn't understand this part. Please tell me more! At least if we add the annotation (or keyword), it won't have any impact on any current codebase because they are not using that new annotation. And it will be tested strictly to prevent serious problems, so to me it seems almost harmless to add features like a new auto-increment function…
That's true. I guess it's some legacy issue and there's an on-going normalization process, and debates as well. Maybe we could discuss it later if we agree on having an implicit flag value function would be a good idea, and go onto the implementation.
Sounds like adding a wrapper at the language level is much more complicated than I thought, and involves a lot of unresolved issues as well. Earlier I was just thinking about the possible impact on performance and memory usage, and how to deal with the difficulties of compatibility with the
Yes, the immutability of the Seems that without a wrapper we cannot have in-place methods. Let's just still call them
I see now. You are right. Is there any chance we can have spread operators for dictionaries as well? So that enums' keys can be unpacked and passed to
But did you just mentioned that a wrapper type |
Tested versions
System information
Godot v4.2.1.stable - macOS 14.3.1 - Vulkan (Forward+) - integrated Apple M2 Max - Apple M2 Max (12 Threads)
Issue description
I have a question about the
@export_flags
bit fields (which are under the hood integers), and not sure if it is a bug or not. The documentation of@export_flags
states that:And also if you try in the engine to give more than 32 arguments to
@export_flags
it will raise the parse error below:So both the documentation and the error message sound like the bit fields can only hold 32 bits of information. However, I also read that in documentation it also says the
int
type has a size of 64 bits or 8B. I also performed some tests on aint
variable that has@export_flags
annotation, it shows that an@export_flags
field can indeed store 64 bits information.I am not sure what makes
@export_flags
32 bits. Is it a bug, a legacy issue, or an intended behavior (for example some compatibility strategy in regard to 32-bit architectures/OS)?Thank you for your clarification!
Steps to reproduce
N/A
Minimal reproduction project (MRP)
N/A
The text was updated successfully, but these errors were encountered: