godotengine / godotengine/godot-docs

C++, the Godot Way

Open
#7,248 1 comment 7 reactions 0 assignees View on GitHub
area:engine details content:new page enhancement topic:gdextension
Dominant language
reStructuredText
Stars
5.7k
Forks
3.8k
Avg merge
1d 20h
Merged PRs (30d)
25

Description

I believe there is a gap in the documentation for C++ development. As I work on my own module, I'm finding types and conventions that seem to not be well documented other than the example that is the entire engine. That helps you figure out _how_ to use various Godot types, but it doesn't tell you _what_ types to use or _why_ use one over another.

The intended audience for this document would be intermediate to advanced C++ programmers who want to contribute to the engine, write C++ modules, or write C++ GDExtentions. The goal is not to teach C++ itself, or to help readers compile the engine. The goal is to help direct readers to the right tools within the engine to solve common problems and match Godot's architecture. When should I use a `List` or an `Array`? What types should I inherit from? What is a `Variant` and how do I use it? Some of these problems already have great GDScript/C# documentation, but many do not have GDScript equivalents.

I have started to write that document. I would like to know if I should continue working on this. There are gaps in my knowledge and I would also like help filling out some of these sections, for my own education and to better the documentation on these features.

People who are very familiar with something tend to have trouble understanding whats confusing about it. People who are very new at something tend to have trouble understanding where to start. At the moment, I am directly in the middle of these people and this is the result of the notes I've taken and what I've learned along this path.

# Writing C++ for Godot Modules, GDNative/GDExtension, and Engine Development

If you're reading this, you're probably REALLY familiar with GDScript and you want to push further into lower level stuff.

This document will not teach you how to compile Godot, how to compile a GDNative/GDExtension DLL, etc. It will not teach you how to write C++.

This document is intended to teach how to write Godot C++. How to use Godot's types and how to convert to and from foreign types where applicable. What common macros exist and how to use them (and when NOT to use them).

# Types

## Overall Type Guidelines

Overall, it is recommended that you use Godot's types over standard library types. [todo: add reasons, I don't actually know]. If you are pulling in an external library foreign to Godot, it's not recommended that you convert the entire library to use Godot types. However, at the interface layer (your glue code), you should try to convert that library's types to Godot types (except where there are significant performance implications).

That means:
- Instead of `std::list`, use `List`.
- Instead of `std::vector`, use `Array`.
- Instead of `std::map`, use `Dictionary`.

If a standard library gives you one of these or other Godot-incompatible type and it is not prohibitively performant to convert them into Godot types, do so before handing them off to the user (GDScript programmer). If you want to preserve the original object or you it would be prohibitively performance expensive to convert, it makes sense to write GDScript-friendly interfaces.

Examples:
- SteamIDs are, under the hood, 64bit `int`s. It's very easy to expose those to Godot's `int`s. This allowed your GDScript to pass SteamIDs with out building a whole new class just for handling them.
- SteamIDs are, under the hood, 64bit `int`s. However, it doesn't make sense to treat them as an `int` as you well *never* do math with them. Functions that expect a SteamID, don't expect just any number, they expect a valid account. A class dedicated to this will help use type safty to keep you from putting junk data into a function.

There are often no right answers here. Do what makes sense for your project.

## Primitives

int, int64, unsigned int
float, float64
strings

### String

When interfacing with external libraries, you'll often be handed C-style strings (a char pointer or array that points to a sequence of data that is `\0` terminated). When interfacing with Godot libraries, they will expect and return Godot `String`s.

[Todo] write a conversion table for converting a Godot `String` to and from each of these:
- C String (null terminated pointer)
- std::string
- Pointer+Size

## Container Types

These types are containers for storing other objects. They are typically templated `` types.

### List<>

Godot's `List<>` type is a Linked List (this is like C/C++'s `std::list`, but it is NOT like C#'s `List` which is actually an array). Adding elements to the start or end of this structure is constant cost. Accessing the `N`th value costs `N*time`.

Use a `List<>` when:
- You have a *First In, First Out* style **queue** where you add elements to one end, and remove them from the other end.
- You *First In, Last Out* style **stack** where you add elements to one end, and remove them from the same end.
- You strictly need to iterate through sequentially.

Do NOT use a `List<>` when:
- You often need to indices in the middle.

[todo] document for loop iterators, DO NOT USE index based accessors!

### Array<>

Godot's `Array<>` type is a GDScript frinedly Array. It's very similar to C/C++ `std::vector`. Accessing the `N`th value costs constant time. Adding and removing elements costs more time for longer arrays.

Use an `Array<>` when:
- You often need to indices in the middle.

Do NOT use an `Array<>` when:
- You often need to add elements to very large Arrays. Adding or removing

### Variant

[TODO]

## Ref, RefCount

[Todo]

### Dictionary

[Todo] I'm going need some help with this one. I believe there are certain rules on what can and cannot be used as a key/value and I'm not sure I fully understand this yet.

## Base Classes

These are types that your classes will want to inherit from. Inheriting from these types gives your types Godot engine features, like reference counting or compatibility with higher level scripting tools. However, all of these types will cost *some* overhead and you shouldn't use them unless the features are worth the overhead.

### Object

`Object` is the base class for any type that can interface with GDScript. You should inherit off `Object` only if no other type makes sense. The default inheritance in GDScript is not `Object`, it's `Resource`.

`Object`s are not reference counted and must be explicated deleted when no longer needed.

### Node : Object

`Node` is the base class for all elements of the Godot scene tree. It contains functionality for scene traversal as well as a number of other features. If you have used GDScript extensively, you are probably very familiar with `Node`. If your class needs to exist inside the scene tree, `Node` or something that inherits from it is probably the right base class.

`Node`s are not reference counted and must be explicated deleted when no longer needed.

### RefCounted

[Todo] get help figuring out when the best time to use RefCounted vs Resource vs RefCount is.

`RefCounted`s are reference counted and will delete themselves when no longer needed. However, they can still result in a memory leak in the case of a cycle (A has a reference B and B has a reference A, or A has a reference to itself).

### Resource : RefCounted

`Resource` is used for data that multiple objects need a shared reference to. Textures, models, etc are typical examples. But `Resource`s.

`Resource`s are reference counted and will delete themselves when no longer needed. However, they can still result in a memory leak in the case of a cycle (A has a reference B and B has a reference A, or A has a reference to itself).

### RefCount

[todo] no idea when and where to use this

# Macros

Godot has a LOT of macros that make writing Godot C++ easer. I'm not going to talk about all of them. But here are a few that are useful and some "gotcha"s that you should look out for.

## `error_macros.h`

The error macros is Godot are pretty powerful for reporting events and warning within Godot at runtime. There are so many of them, that I don't think it makes sense to go through them all. We will have go over the various types.

- `ERR_` denotes an error. You will see red text in the editor console.
- ...`FAIL` adds a `return;`. Used for errors that should just your current function without continuing.
- ...`FAIL_V(m_retval)` adds a `return m_retval;`
- ...`CONTINUE` adds a `continue;` to your error. Skips just this iteration of a loop.
- ...`BREAK` ads a `break;` to the end of your error. Skips the reset of the loop.
- `MSG(m_msg)` adds a `m_msg` to the error message.
- `COND(m_cond)` adds a `if (m_cond == true)` to the error checker. This error will only happen if this condition is true. This is mostly what you'll use.
- `PRINT(m_msg)` denotes a function that does NOT affect control flow (return, break, or continue), but will still dump `m_msg` as an error message.
- `WARN(m_msg)` denotes a warning. You will see yellow text in the editor console and the function will continue. Implies `PRINT`.
- `ONCE` is used in `WARN_` and `ERR_PRINT_` to denote an error/warning message that should only print once. You will only see an error/warning the *first* time this line hits. The line will still run, but you won't create additional messages.
- `ED` or `EDMSG` denotes when an error creates a notification, which pops up in the lower right of the editor. Outside of the editor, this acts just like its non `ED` counterpart.

The error macros in `error_macros.h` are built of several of these tags at once. For example this example, we can replace a null check, an error message, and a return value with one line that does all three in a way that is very readable for anyone that needs to review this later.

```C++
Error MyClass::do_something(){
if(this->something==NULL){
print_error("Something was not initialized properly!");
return ERR_DOES_NOT_EXIST;
}
//...do something!
}
Error MyClass::do_something_better(){
ERR_FAIL_COND_V_MSG(something==null, ERR_DOES_NOT_EXIST, "Something was not initialized properly!")
//...do something!
}
```

Review `error_macros.h` to get a better sense of what's available. Most autocomplete in IDEs should give you a good sense of what's available if you try to combine some of these tags.

## GDCLASS(m_class, m_inherits)

`GDCLASS(m_class, m_inherits)` is used to register your class with Godot's ClassDB.

[todo] expand that various `_bind_method` types. enums? enum class?

# .clang-format

`.clang-format` can be copied directly from the Godot repo into your C++ project and will allow compatible linters to help you autoformat your code to match Godot's style guides. It won't cover every formatting case, but it will do a good job at first pass. You might not *like* the formatting rules (tabs vs spaces, newlines at open brace, etc ), as is often the case, consistency is more valuable than correctness.

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.