Godot version
4.0
System information
Arch Linux
Issue description
Say I have a GDExtension class Foo:
class Foo : public godot::Node {
GDCLASS(Foo, godot::Node);
protected:
static void _bind_methods();
public:
void _enter_tree() override;
void hello();
};
void Foo::_bind_methods() {
ClassDB::bind_method(D_METHOD("hello"), &Foo::hello);
}
void Foo::_enter_tree() {
UtilityFunctions::print("Foo::_enter_tree");
}
void Foo::hello() {
UtilityFunctions::print("Foo::hello");
}
If I instantiate Foo in C++ using new, the resulting object seems to work at first; I can add it to the scene tree, and add a Sprite2D under it which appears normally:
var f : Foo = … # Foo created in C++ with new
add_child(f)
var s = Sprite2D.new()
s.position = Vector2(100,100)
s.texture = …
f.add_child(s)
But it misbehaves in surprising ways: Foo::_enter_tree() doesn't get called for add_child(f), and if I try to call f.hello(), the game crashes.
Foo objects instantiated with memnew don't have this problem.
If I'm reading the code correctly, every extension object (Foo in this example) gets assigned a corresponding Godot engine object (Node in this example). Each contains an opaque pointer to the other:
Foo inherits from Wrapped. The Wrapped constructor sets Wrapped::_owner to point to the Node.
Node inherits from Object. ClassDB::set_object_extension_instance sets Object::_extension_instance to point to the Foo.
It seems like it shouldn't matter how I instantiate Foo - new Foo, memnew(Foo), and std::make_unique<Foo>() should all call the Wrapped constructor.
Although, the existence of the _extension_instance pointer implies some limitations on how I can use Foo. For example, a std::vector<Foo> presumably wouldn't work, since vector reallocation would
invalidate the _extension_instance of each corresponding Node.
It's not clear to me exactly how I'm allowed to use GDExtension objects, and so I can't tell whether this is a Godot bug or a documentation bug. Maybe new Foo isn't supposed to work in the first place?
Specifically, which of the following is allowed?
- instantiating a
Foo with new (as above)
- freeing a
Foo with delete (throws exception if the Foo was created with memnew!)
- putting
Foo objects in a reallocating container like std::vector<Foo>
- handling
Foo with a managed pointer like std::unique_ptr<Foo>
- including
Foo as a field of another GDExtension class, like:
class Bar : public godot::Node {
GDCLASS(Bar, godot::Node);
Foo f;
};
- including
Foo as a field of a regular class, like:
If any of these uses are forbidden, they should be called out in the documentation.
Steps to reproduce
The attached project has the above code, and some other test functions.
- unzip
make-node-test.zip
- check out godot-cpp into
make-node-test/extension/godot-cpp/
cd make-node-test/extension/out/
cmake .. and build, e.g. CC=gcc CXX=g++ cmake -G Ninja .. && ninja
cp libfoo.so ../../project/
<Godot executable> --path ../../project/
Minimal reproduction project
make-node-test.zip
Godot version
4.0
System information
Arch Linux
Issue description
Say I have a GDExtension class
Foo:If I instantiate
Fooin C++ usingnew, the resulting object seems to work at first; I can add it to the scene tree, and add a Sprite2D under it which appears normally:But it misbehaves in surprising ways:
Foo::_enter_tree()doesn't get called foradd_child(f), and if I try to callf.hello(), the game crashes.Fooobjects instantiated withmemnewdon't have this problem.If I'm reading the code correctly, every extension object (
Fooin this example) gets assigned a corresponding Godot engine object (Nodein this example). Each contains an opaque pointer to the other:Fooinherits fromWrapped. TheWrappedconstructor setsWrapped::_ownerto point to theNode.Nodeinherits fromObject.ClassDB::set_object_extension_instancesetsObject::_extension_instanceto point to theFoo.It seems like it shouldn't matter how I instantiate
Foo-new Foo,memnew(Foo), andstd::make_unique<Foo>()should all call theWrappedconstructor.Although, the existence of the
_extension_instancepointer implies some limitations on how I can useFoo. For example, astd::vector<Foo>presumably wouldn't work, since vector reallocation wouldinvalidate the
_extension_instanceof each correspondingNode.It's not clear to me exactly how I'm allowed to use GDExtension objects, and so I can't tell whether this is a Godot bug or a documentation bug. Maybe
new Fooisn't supposed to work in the first place?Specifically, which of the following is allowed?
Foowithnew(as above)Foowithdelete(throws exception if theFoowas created withmemnew!)Fooobjects in a reallocating container likestd::vector<Foo>Foowith a managed pointer likestd::unique_ptr<Foo>Fooas a field of another GDExtension class, like:Fooas a field of a regular class, like:If any of these uses are forbidden, they should be called out in the documentation.
Steps to reproduce
The attached project has the above code, and some other test functions.
make-node-test.zipmake-node-test/extension/godot-cpp/cd make-node-test/extension/out/cmake ..and build, e.g.CC=gcc CXX=g++ cmake -G Ninja .. && ninjacp libfoo.so ../../project/<Godot executable> --path ../../project/Minimal reproduction project
make-node-test.zip