You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In virtual classes, the destructor is a virtual function like any other that should be overrideable - this allows language projections to free resources when the object is deleted by third-party "owners" of the pointer, for example when it's part of a UI hierarchy where memory is managed by QT - a problem hinted at #17.
For this technique to be useful, the language projection also need to be able to place custom data in the derived class "somehow" (in c++, these would simply be additional members in the class).
No matter the style, all callbacks (virtual function overrides / destructor / signal handlers) would need access to this user data.
I'm contemplating 3 different models for this:
"user" or "fat" pointers - here, the bindings pass in some arbitrary data that is passed back to the binding together with the qobject pointer.
This is a variation on the above theme where the "callback functions" are part of the data passed into the instance, potentially allowing different callback functions per "user-derived type". Per-instance "Custom data" for the binding can reside together with the VTable - a downside of this approach is that the vtable part often is static / known at compile time but per-instance data is not, so it's a little wasteful.
Instead of placing a pointer in the instance, one can simply pass in an "extra size" parameter that is used when allocating the derived class - this results in a memory layout similar to what inheriting fromMiqtVirtualQbject would result in. It's a little bit more obscure as far as solutions go but has some ergonomic and performance advantages (single pointer, single allocation).
n.b. QObject_userdata is an implementation detail not meant to be exposed in bindings - it would suffer the same dynamic_cast/panic problem as discussed here. Instead, we're careful to expose userdata only in contexts where the instance is guaranteed to hold the right type.
const VTable + extra size
This is the "most flexible" approach and perhaps the one closest to what goes on behind the c++ scenes: the vtable is passed in as a pointer-to-const while extrasize is used for "user data" - this way, bindings can pre-allocate a set of vtables for each derived class and still place (mutable) data in each instance.
I'm leaning towards 3) or 4) for the nim bindings the technique is probably useful for all languages / bindings. 4) potentially has a little bit of extra flexibility but the const vtable is very similar to the "link-time callbacks" that miqt uses today and the "user data" already offers enough flexibility to handle this on the projection side - thoughts?
The text was updated successfully, but these errors were encountered:
A 5th trick for this is to connect to the QObject::destroyed signal, and do everything inside the language projection.
I thought of this but it doesn't quite adhere to the natural C++ destructor order and thus breaks the "inheritance" illusion in subtle ways that I'd rather avoid.
In virtual classes, the destructor is a virtual function like any other that should be overrideable - this allows language projections to free resources when the object is deleted by third-party "owners" of the pointer, for example when it's part of a UI hierarchy where memory is managed by QT - a problem hinted at #17.
For this technique to be useful, the language projection also need to be able to place custom data in the derived class "somehow" (in c++, these would simply be additional members in the class).
No matter the style, all callbacks (virtual function overrides / destructor / signal handlers) would need access to this user data.
I'm contemplating 3 different models for this:
This is a variation on the above theme where the "callback functions" are part of the data passed into the instance, potentially allowing different callback functions per "user-derived type". Per-instance "Custom data" for the binding can reside together with the VTable - a downside of this approach is that the vtable part often is static / known at compile time but per-instance data is not, so it's a little wasteful.
Instead of placing a pointer in the instance, one can simply pass in an "extra size" parameter that is used when allocating the derived class - this results in a memory layout similar to what inheriting from
MiqtVirtualQbject
would result in. It's a little bit more obscure as far as solutions go but has some ergonomic and performance advantages (single pointer, single allocation).n.b.
QObject_userdata
is an implementation detail not meant to be exposed in bindings - it would suffer the samedynamic_cast
/panic problem as discussed here. Instead, we're careful to exposeuserdata
only in contexts where the instance is guaranteed to hold the right type.This is the "most flexible" approach and perhaps the one closest to what goes on behind the c++ scenes: the vtable is passed in as a pointer-to-const while extrasize is used for "user data" - this way, bindings can pre-allocate a set of vtables for each derived class and still place (mutable) data in each instance.
I'm leaning towards 3) or 4) for the nim bindings the technique is probably useful for all languages / bindings. 4) potentially has a little bit of extra flexibility but the const vtable is very similar to the "link-time callbacks" that miqt uses today and the "user data" already offers enough flexibility to handle this on the projection side - thoughts?
The text was updated successfully, but these errors were encountered: