godotengine / godotengine/godot-cpp

Docs: GDnative Reference Counting

Open
#281 2 comments 7 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
2.7k
Forks
809
Avg merge
1d 3h
Merged PRs (30d)
8

Description

Request for documentation

I'm having a hard time figuring out the rules for dealing with references to other nodes and types in GDnative.

Documentation on this seems to be sparse and I'm worried about producing crashes (*by not incrementing the reference counter when taking ownership of something*) or leaking memory (*because I'm not releasing references properly*).

It would be very useful to provide some documentation, or even just a set of rules, on how to deal with references in GDnative.

The following 3 question cover most of what I feel is unclear right now and having answers to them would help a great deal:

1. **Let's assume I had a loose class that's not a node. Should I inherit from `godot::Reference` (refcounted) or from `godot::Object` (not refcounted)?**

```
class Move : public godot::Reference {
GODOT_CLASS(Move, godot::Reference)
};
```

1a. If the class inherits from `godot::Object`, how is ownership handled?

```
# GDscript
func step1() -> void:
var some_move = Move.new()
cool_gdnative_object.set_move(some_move)

# Will some_move get freed at this point?
# Can cool_gdnative_object hold on to some_move or is ownership transfer not possible?
```

1b. If the class inherits from `godot::Reference`, when do I increment/decrement the reference counter?

```
# GDnative
void CoolGdnativeClass::set_move(Move *some_move) {
this->activeMove = some_move;

// I haven't incremented some_move's reference counter.
// Should I, so it doesn't get freed while I'm still holding the pointer?
}
```

2. **How do I correctly handle the reference counter when I return a new object?**

```
# GDnative
Move *CoolGdnativeClass::create_awesome_move() {
Move *awesomeMove = AwesomeMove::_new(); // reference counter == 1?
return awesomeMove; // ownership transfer to caller
}
```

If a new object starts out with a reference count of 1, then I can't decrement it before returning the object.

Thus, it follows that returning an object must also share ownership:

- If I instead returned an object that I was holding (i.e. `this->active_move`), I'd have to increment the reference counter before doing so

- When I call any method that returns an object, I need to decrement the reference counter on the returned object after I'm done with it.

3. **Godot nodes do not inherit from `Reference`**

```
class CameraController : public godot::Node {
GODOT_CLASS(CameraController, godot::Node)

private: godot::Ref target; // doesn't compile
}
```

Does this mean nodes always belong to the scene tree?

If so, wouldn't it be dangerous to store references to nodes because if the node gets destroyed (i.e. enemy dies, etc.), anyone holding a reference to it would have a dangling pointer?

Or do it need to get some 'instance id', 'rid' or somesuch and always look it up via Godot's scene server?

**Existing documentation** (or rather, attempts to find it :))

Godot Variants do call `.ref.unref()` on objects during `clear()`
https://github.com/godotengine/godot/blob/master/core/variant.cpp#L1107-L1111

Godot's `reference.h` contains a helper class `Ref` which does manipulate reference counters
https://github.com/godotengine/godot/blob/master/core/reference.h#L64

godot-cpp has a `Ref` class with a comment that states it's replicating Godot's own `Ref` class
https://github.com/GodotNativeTools/godot-cpp/blob/master/include/core/Ref.hpp#L10

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.