Skip to content

Custom type converters for GDExtension #14507

@YakoYakoYokuYoku

Description

@YakoYakoYokuYoku

Problem or limitation

I have a library with its own vector types and I'd like to convert them to their Godot counterparts so I can use them with the API in an extension. I cannot modify the library to suit Godot, though.

// That's how it looks from a header.

struct Vector {
  float x;
  float y;
  float z;
};

And in a file that implements conversions for the GDExtension.

// I can use these functions to convert types back and forth.

godot::Vector3 vector_to_vector3(const Vector &v) {
  // ...
}

Vector vector_from_vector3(const godot::Vector3 &v) {
  // ...
}

So far so good, but if I have hundreds or thousands of methods I'll have to place many calls of these conversion functions.

// Repeat this as required. You can also use code generation, but the problem is still there.

godot::Vector3 Entity::get_position() const {
  return vector_to_vector3(wrapped.position());
}

void Entity::set_position(const godot::Vector3 &v) {
  wrapped.position() = vector_from_vector3(v);
}

static void Entity::_bind_methods() {
  godot::ClassDB::bind_method(godot::D_METHOD("get_position"), &Entity::get_position);
  godot::ClassDB::bind_method(godot::D_METHOD("set_position", "position"), &Entity::set_position);
}

Proposed improvement

I suggest to add a macro which registers these converters so that they are used under the hood. Godot has a similar thing in form of the PtrToArg template, but that's only for converting a handful of types for function arguments and return values. On the other hand, this registration also lets the programmer to convert between registered types and variants.

// This registers the converters so that they can be used in method bindings, variants, etc.
// We pass both `vector_to_vector3` and `vector_from_vector3` here.

REGISTER_CONVERTER(Vector, godot::Vector3, vector_to_vector3, vector_from_vector3);

// Notice that we use the library types and that the converters are called elsewhere.

Vector Entity::get_position() const {
  return wrapped.position();
}

void Entity::set_position(const Vector &v) {
  wrapped.position() = v;
}

void Entity::_bind_methods() {
  godot::ClassDB::bind_method(godot::D_METHOD("get_position"), &Entity::get_position);
  godot::ClassDB::bind_method(godot::D_METHOD("set_position", "position"), &Entity::set_position);
}

A typical case would be string classes, which there are many of these out there.

REGISTER_CONVERTER(utf::String, godot::String, utf_to_string, utf_from_string);

And not only for builtins, it can also be used to register other types that are wrapped in Godot objects too.

REGISTER_CONVERTER(std::shared_ptr<Sample>, godot::Object, sample_shared_to_object, sample_shared_from_object);
REGISTER_CONVERTER(std::unique_ptr<Sample>, godot::Object, sample_unique_to_object, sample_unique_from_object);

Although this has to be taken with care while taking into consideration that reference to the original Godot object can be lost.

Proposal review

  • Yes, this proposal is highly specific and actionable.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions