-
-
Notifications
You must be signed in to change notification settings - Fork 572
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
Fix deriving a custom class with virtual methods #855
Fix deriving a custom class with virtual methods #855
Conversation
533cd04
to
24deaa3
Compare
Actually, I have a doubt. Back to the case: class TestNode : public godot::Node {
GDCLASS(TestNode, godot::Node)
public:
void _ready() override;
private:
static void _bind_methods();
};
class DerivedNode : public TestNode {
GDCLASS(DerivedNode, TestNode)
private:
static void _bind_methods();
}; With this PR, registering (on a side note, I'm confused at how |
Why should it bind |
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.
Seems fine to me.
Because there is, in the base class. If one creates a |
Gave it a go, |
Does it work with multiple levels of derived nodes? |
Which combination do you want to try? |
I wanted to check this: class A : public godot::Node {
GDCLASS(A, godot::Node);
static void _bind_methods();
public:
void _ready() override;
};
class B : public A {
GDCLASS(B, A);
static void _bind_methods();
};
class C : public B {
GDCLASS(C, B);
static void _bind_methods();
}; |
To recap here, in that setup, when registered in ClassDB, Actually, I see GDNativeExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, const char *p_name) {
const char *class_name = (const char *)p_userdata;
std::unordered_map<std::string, ClassInfo>::iterator type_it = classes.find(class_name);
ERR_FAIL_COND_V_MSG(type_it == classes.end(), nullptr, "Class doesn't exist.");
const ClassInfo *type = &type_it->second;
while (type != nullptr) {
std::unordered_map<std::string, GDNativeExtensionClassCallVirtual>::const_iterator method_it = type->virtual_methods.find(p_name);
if (method_it != type->virtual_methods.end()) {
return method_it->second;
}
type = type->parent_ptr;
}
return nullptr;
} On the other hand, if every method of the hierarchy had to be registered via template calls, it's annoying because the code may have to become significantly more complex. Because I could start by changing this: static void register_own_virtuals() {
m_inherits::register_virtuals<m_class, m_inherits>();
}
template <class T, class B>
static void register_virtuals() {
m_inherits::register_own_virtuals(); // First register base class virtuals
m_inherits::register_virtuals<T, B>(); // Then attempt to register virtuals of the caller on top
} This goes recursively such that all the inheritance hierarchy gets to register its virtual functions, and child classes would do so "on top" of it. B::register_virtuals<B, A>()
A::register_own_virtuals();
Node::register_virtuals<A, Node>();
bind_virtual_method(A::_ready)
A::register_virtuals<B, A>();
Node::register_own_virtuals();
// Nothing, empty function
Node::register_virtuals<B, A>();
// Nothing, B doesn't override `_ready` However with C::register_virtuals<C, B>()
B::register_own_virtuals()
A::register_own_virtuals();
Node::register_virtuals<A, Node>();
bind_virtual_method(A::_ready)
A::register_virtuals<B, A>();
Node::register_own_virtuals();
// Nothing
Node::register_virtuals<B, A>();
// Nothing, B doesn't override `_ready`
B::register_virtuals<C, B>();
A::register_own_virtuals();
Node::register_virtuals<A, Node>();
bind_virtual_method(A::_ready)
A::register_virtuals<C, B>();
Node::register_own_virtuals();
// Nothing
Node::register_virtuals<C, B>();
// Nothing, `C` doesn't override `_ready` And with more overriden virtuals it would keep calling multiple times godot-cpp/src/core/class_db.cpp Lines 296 to 306 in 8670305
So modifying |
24deaa3
to
5de2c05
Compare
Made Note:
|
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 gave this a test with my attempt to port 1D to GDExtension and it fixes one of the critical issues there.
Thanks! |
Fixes #854