-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
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
Allow CallableCustom objects to be created from GDExtensions #79005
Conversation
53f9e2d
to
9557478
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
In general, these changes makes sense to me. I just have a couple of questions on some minor points.
9557478
to
acd9f4a
Compare
This is looking really good to me now! @touilleMan @Bromeon Will this work for your bindings? My first pass at implementing this (in a previous PR) was pretty C++-centric, so it'd be good to get some more perspectives on the API :-) |
I think we may need to update the comparison functions in light of this recently merged PR #72346 |
I'm not sure there's any relevance to our implementation of custom callables here. Pointers we are comparing are in the very likely case pointing to static memory, and the performance issue mentioned looks to be an artifact of the container rather than the comparisons. |
Ah, yes, you're right. I misinterpreted what that PR was doing. I thought it was switching away from comparing callables by their pointer addresses (which we're also doing in this PR) to comparing them by some value. But it's actually just comparing the addresses byte-by-byte, but now in reverse order. :-) |
This PR needs a rebase due to conflicts with recent changes |
Co-authored-by: David Snopek <dsnopek@gmail.com>
acd9f4a
to
e0ee985
Compare
Please 🙏 we need this ASAP in 4.2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks reasonable to me with a few minor nits.
A few questions about how this works in practice, though I feel like that's just me not having used Callables much yet. The Object
that is passed in is never sent back to the Extension. Is this by design? Should it be sent as a parameter to call_func? Or should there be a callable_custom_get_object
function?
Second, what should be passed into gdextension_callable_custom_create
's r_callable
parameter? I'm assuming it's a pointer to some amount of allocated memory (enough for a Callable)? Can we be clearer about how much memory that is?
Also, how do you delete the returned callable?
return out; | ||
} | ||
} | ||
return "<CallableCustom>"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it makes sense to have this say <CallableCustomExtension>
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure the distinction between CallableCustomExtension
used for GDExtension and CallableCustom
used by Godot makes much difference here, if more clarity is needed it would be better to provide a to_string_func
with any needed details about the callable.
typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); | ||
|
||
typedef struct { | ||
/* Only `call_func` and `token` are strictly required, however, `object` should be passed if its not a static method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: it's
Thanks for reviewing!
The If your GDExtension actually needs the
We don't need one, because you can call
Yes, it's a pointer to a buffer of 16 bytes, which is defined by I don't know that we need explain this in
The same as any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, @dsnopek I think that all makes sense.
I do think there might be a need to be clear about what that GDExtensionUninitializedTypePtr is. The only other places I really see it are in constructors where you're passing in the type. This one seems a little bit more confusing to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feature is in high demand - it continues to come up on a regular basis. More testing and review would of course be great, but I've personally thought this was ready since mid-July :-)
This is actually incorrect, tho it seems like it should be true. The discussion has reminded me about this bit of workaround code from my extension: https://github.com/maiself/godot-python-extension/blob/master/src/variant/callable.cpp#L12 Basically Godot has checks for |
Hm. Looking at |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested it with Rust and got a basic use case working! 🎉
Here is a simple test case demonstrating it. I didn't test every possible interaction, but calling and destruction seems to work. Any other potential issues would likely show up soon 😎
@dsnopek, I looked a bit thru the code, here are some examples where object-less callables are rejected: I haven't looked to see if there are more cases (I merely searched
|
Ah, ok, great finds! Would be good to fix in a follow up PR :-) |
I've created a separate issue to keep track of it: #81887. Not sure when I can get a PR together, I'd want to make sure all cases are found and no new bugs introduced, so might take a little time to be thorough. |
When is the Operator My custom On the other hand, |
|
You mean in Godot's own C++ code? Because that ordering relation isn't anyhow exposed to GDExtension, as
Is it called for you? If yes, in which context? I tried 3 different scenarios, none of which calls my custom
|
Thanks! Welp it seems like I merged the PR mid conversation, I had it queued and forgot to check the latest status. Let me know if the discussion calls for a follow-up issue or PR, or points at an actual breaking problem in this current implementation. Edit: Reading through, I think this was all discussion of future changes needed and not blocking for this PR. |
No worries, I think it's great to have an initial version merged, then I can also integrate my work so far into godot-rust's I continued in #81901. |
Moved comment to #81901 |
Based off of @dsnopek's work in #78813. This exposes a few more methods from the
CallableCustom
interface.I was originally going to just comment on the original patch, but ended up needing to actually test some ideas out. I don't know if there a better way of submitting a derived patch like this, please let me know if there is!
The changes here were needed for better support in converting Python callables:
hash_func
andequal_func
are needed as objects representing functions in Python can have the same value (compare equal) but not necessarily have the same identity (differing object pointers). Bound method instances are an example. Weak references to callables as well asweakref.WeakMethod
are another case where hashing and comparing pointers is insufficient.Without proper hashes and comparisons connecting and disconnecting Callables to Signals is unreliable.
to_string_func
and the new default of"CallableCustom"
greatly improve printing of values. Callables would print as just an empty string before.callable_custom_get_userdata
was added to allow getting the original object back out of a callable, useful for storing metadata or round trip extraction of callables.Only minimal changes would be needed to godotengine/godot-cpp#1155 as the defaults should still suffice.
Thanks to @dsnopek for putting the original together! Please feel free to take over the code again.