Working on a Godot 4 addon with GDExtension, I find that I cannot compile my source code due to type errors that I don't know exactly how to work around or fix.
I have the following code:
// register_types.cpp
#include "register_types.h"
#include "../godot-cpp/gdextension/gdextension_interface.h"
#include "../godot-cpp/include/godot_cpp/core/class_db.hpp"
#include "../godot-cpp/include/godot_cpp/core/defs.hpp"
#include "../godot-cpp/include/godot_cpp/godot.hpp"
#include "poker.hpp"
void initialize_gdextension_types(godot::ModuleInitializationLevel p_level) {
if (p_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
godot::ClassDB::register_class<Poker>();
godot::ClassDB::register_class<SingleHandRankingEvaluationResult>();
}
void uninitialize_gdextension_types(godot::ModuleInitializationLevel p_level) {
if (p_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C" {
// Initialization
// NOTE: I don't think we need to touch anything here
GDExtensionBool GDE_EXPORT
poker_utils_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address,
GDExtensionClassLibraryPtr p_library,
GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library,
r_initialization);
init_obj.register_initializer(initialize_gdextension_types);
init_obj.register_terminator(uninitialize_gdextension_types);
init_obj.set_minimum_library_initialization_level(
godot::MODULE_INITIALIZATION_LEVEL_CORE);
return init_obj.init();
}
}
// poker.hpp
#include "../godot-cpp/gdextension/gdextension_interface.h"
#include "../godot-cpp/include/godot_cpp/core/class_db.hpp"
#include "../godot-cpp/include/godot_cpp/core/defs.hpp"
#include "../godot-cpp/include/godot_cpp/godot.hpp"
#include "../godot-cpp/include/godot_cpp/templates/vector.hpp"
class Poker : public godot::Object {
GDCLASS(Poker, godot::Object);
protected:
static void _bind_methods();
public:
static godot::PackedStringArray getDeck();
static godot::PackedStringArray getDeckShuffled();
static SingleHandRankingEvaluationResult
singleHandRankEvaluation(const godot::PackedStringArray cards);
};
// ...
struct SingleHandRankingEvaluationResult : public godot::Object {
GDCLASS(SingleHandRankingEvaluationResult, godot::Object);
private:
int HandRanking;
godot::String Error;
protected:
static void _bind_methods();
public:
void set_hand_ranking(int ranking);
int get_hand_ranking();
void set_error(godot::String error);
godot::String get_error();
};
// poker.cpp
#include "poker.hpp"
#include "../src/deck.hpp"
#include "../src/hand-evaluation.hpp"
#include <string>
#include <vector>
void SingleHandRankingEvaluationResult::set_hand_ranking(int ranking) {
this->HandRanking = ranking;
}
int SingleHandRankingEvaluationResult::get_hand_ranking() {
return this->HandRanking;
}
void SingleHandRankingEvaluationResult::set_error(godot::String error) {
this->Error = error;
}
godot::String SingleHandRankingEvaluationResult::get_error() {
return this->Error;
};
void SingleHandRankingEvaluationResult::_bind_methods() {
godot::ClassDB::bind_method(
godot::D_METHOD("set_hand_ranking"),
&SingleHandRankingEvaluationResult::set_hand_ranking);
godot::ClassDB::bind_method(
godot::D_METHOD("get_hand_ranking"),
&SingleHandRankingEvaluationResult::get_hand_ranking);
godot::ClassDB::bind_method(godot::D_METHOD("set_error"),
&SingleHandRankingEvaluationResult::set_error);
godot::ClassDB::bind_method(godot::D_METHOD("get_error"),
&SingleHandRankingEvaluationResult::get_error);
godot::ClassDB::add_property(
"SingleHandRankingEvaluationResult",
godot::PropertyInfo(godot::Variant::INT, "hand_ranking"),
"set_hand_ranking", "get_hand_ranking");
godot::ClassDB::add_property(
"SingleHandRankingEvaluationResult",
godot::PropertyInfo(godot::Variant::STRING, "error"), "set_error",
"get_error");
}
SingleHandRankingEvaluationResult
Poker::singleHandRankEvaluation(const godot::PackedStringArray cards) {
poker::Cards all_cards;
for (const auto &card : cards) {
all_cards.push_back(std::string(godot2stdstr(card)));
}
auto result = poker::singleHandRankEvaluation(all_cards);
if (std::holds_alternative<poker::Error>(result)) {
SingleHandRankingEvaluationResult evaluation;
evaluation.set_error(std::get<poker::Error>(result).message.c_str());
return evaluation;
}
int ranking = std::get<int>(result);
SingleHandRankingEvaluationResult evaluation;
evaluation.set_hand_ranking(ranking);
return evaluation;
};
when running scons
I get these errors:
➜ scons
scons: Reading SConscript files ...
Auto-detected 12 CPU cores available for build parallelism. Using 11 cores by default. You can override it with the -j argument.
Building for architecture x86_64 on platform linux
scons: done reading SConscript files.
scons: Building targets ...
scons: `godot-cpp/bin/libgodot-cpp.linux.template_debug.x86_64.a' is up to date.
g++ -o godot/poker.os -c -std=c++17 -fPIC -Wwrite-strings -m64 -march=x86-64 -O2 -fPIC -DLINUX_ENABLED -DUNIX_ENABLED -DDEBUG_ENABLED -DDEBUG_METHODS_ENABLED -DNDEBUG -Igodot-cpp/gdextension -Igodot-cpp/include -Igodot-cpp/gen/include -I/nix/store/5hch4vqzwan8ksab4pfpyrbb7wvvv3ad-cxxtest-4.4/include -I/nix/store/7nbh3b6hhjqjs3nfy529g38l85iv46i9-openssl-3.0.11-dev/include -Isrc -Iompeval/omp -Igodot godot/poker.cpp
In file included from godot/../godot-cpp/include/godot_cpp/core/class_db.hpp:38,
from godot/poker.hpp:5,
from godot/poker.cpp:1:
godot-cpp/include/godot_cpp/core/method_bind.hpp: In instantiation of 'GDExtensionVariantType godot::MethodBindTRS<R, P>::gen_argument_type(int) const [with R = SingleHandRankingEvaluationResult; P = {godot::PackedStringArray}]':
godot-cpp/include/godot_cpp/core/method_bind.hpp:674:33: required from here
godot-cpp/include/godot_cpp/core/method_bind.hpp:678:71: error: incomplete type 'godot::GetTypeInfo<SingleHandRankingEvaluationResult, void>' used in nested name specifier
678 | return GDExtensionVariantType(GetTypeInfo<R>::VARIANT_TYPE);
| ^~~~~~~~~~~~
godot-cpp/include/godot_cpp/core/method_bind.hpp: In instantiation of 'godot::PropertyInfo godot::MethodBindTRS<R, P>::gen_argument_type_info(int) const [with R = SingleHandRankingEvaluationResult; P = {godot::PackedStringArray}]':
godot-cpp/include/godot_cpp/core/method_bind.hpp:682:23: required from here
godot-cpp/include/godot_cpp/core/method_bind.hpp:688:62: error: incomplete type 'godot::GetTypeInfo<SingleHandRankingEvaluationResult, void>' used in nested name specifier
688 | return GetTypeInfo<R>::get_class_info();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
godot-cpp/include/godot_cpp/core/method_bind.hpp: In instantiation of 'GDExtensionClassMethodArgumentMetadata godot::MethodBindTRS<R, P>::get_argument_metadata(int) const [with R = SingleHandRankingEvaluationResult; P = {godot::PackedStringArray}]':
godot-cpp/include/godot_cpp/core/method_bind.hpp:697:49: required from here
godot-cpp/include/godot_cpp/core/method_bind.hpp:701:48: error: incomplete type 'godot::GetTypeInfo<SingleHandRankingEvaluationResult, void>' used in nested name specifier
701 | return GetTypeInfo<R>::METADATA;
| ^~~~~~~~
In file included from godot-cpp/include/godot_cpp/core/method_bind.hpp:34:
godot-cpp/include/godot_cpp/core/binder_common.hpp: In instantiation of 'void godot::call_with_variant_args_static_ret(R (*)(P ...), const Variant**, Variant&, GDExtensionCallError&, IndexSequence<Is ...>) [with R = SingleHandRankingEvaluationResult; P = {PackedStringArray}; long unsigned int ...Is = {0}]':
godot-cpp/include/godot_cpp/core/binder_common.hpp:584:35: required from 'void godot::call_with_variant_args_static_ret_dv(R (*)(P ...), const void* const*, int, Variant&, GDExtensionCallError&, const std::vector<Variant>&) [with R = SingleHandRankingEvaluationResult; P = {PackedStringArray}; GDExtensionConstVariantPtr = const void*]'
godot-cpp/include/godot_cpp/core/method_bind.hpp:707:39: required from 'godot::Variant godot::MethodBindTRS<R, P>::call(GDExtensionClassInstancePtr, const void* const*, GDExtensionInt, GDExtensionCallError&) const [with R = SingleHandRankingEvaluationResult; P = {godot::PackedStringArray}; GDExtensionClassInstancePtr = void*; GDExtensionConstVariantPtr = const void*; GDExtensionInt = long int]'
godot-cpp/include/godot_cpp/core/method_bind.hpp:705:18: required from here
godot-cpp/include/godot_cpp/core/binder_common.hpp:546:15: error: no match for 'operator=' (operand types are 'godot::Variant' and 'SingleHandRankingEvaluationResult')
546 | r_ret = (p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
| ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from godot-cpp/include/godot_cpp/core/property_info.hpp:38,
from godot-cpp/include/godot_cpp/core/object.hpp:36,
from godot-cpp/include/godot_cpp/core/method_ptrcall.hpp:36,
from godot-cpp/include/godot_cpp/core/binder_common.hpp:36:
godot-cpp/include/godot_cpp/variant/variant.hpp:252:18: note: candidate: 'godot::Variant& godot::Variant::operator=(const godot::Variant&)'
252 | Variant &operator=(const Variant &other);
| ^~~~~~~~
godot-cpp/include/godot_cpp/variant/variant.hpp:252:43: note: no known conversion for argument 1 from 'SingleHandRankingEvaluationResult' to 'const godot::Variant&'
252 | Variant &operator=(const Variant &other);
| ~~~~~~~~~~~~~~~^~~~~
godot-cpp/include/godot_cpp/variant/variant.hpp:253:18: note: candidate: 'godot::Variant& godot::Variant::operator=(godot::Variant&&)'
253 | Variant &operator=(Variant &&other);
| ^~~~~~~~
godot-cpp/include/godot_cpp/variant/variant.hpp:253:38: note: no known conversion for argument 1 from 'SingleHandRankingEvaluationResult' to 'godot::Variant&&'
253 | Variant &operator=(Variant &&other);
| ~~~~~~~~~~^~~~~
godot-cpp/include/godot_cpp/core/binder_common.hpp: In instantiation of 'void godot::call_with_ptr_args_static_method_ret_helper(R (*)(P ...), const void* const*, void*, IndexSequence<Is ...>) [with R = SingleHandRankingEvaluationResult; P = {PackedStringArray}; long unsigned int ...Is = {0}; GDExtensionConstTypePtr = const void*]':
godot-cpp/include/godot_cpp/core/binder_common.hpp:594:54: required from 'void godot::call_with_ptr_args_static_method_ret(R (*)(P ...), const void* const*, void*) [with R = SingleHandRankingEvaluationResult; P = {PackedStringArray}; GDExtensionConstTypePtr = const void*]'
godot-cpp/include/godot_cpp/core/method_bind.hpp:713:39: required from 'void godot::MethodBindTRS<R, P>::ptrcall(GDExtensionClassInstancePtr, const void* const*, GDExtensionTypePtr) const [with R = SingleHandRankingEvaluationResult; P = {godot::PackedStringArray}; GDExtensionClassInstancePtr = void*; GDExtensionConstTypePtr = const void*; GDExtensionTypePtr = void*]'
godot-cpp/include/godot_cpp/core/method_bind.hpp:711:15: required from here
godot-cpp/include/godot_cpp/core/binder_common.hpp:589:28: error: 'encode' is not a member of 'godot::PtrToArg<SingleHandRankingEvaluationResult>'
589 | PtrToArg<R>::encode(p_method(PtrToArg<P>::convert(p_args[Is])...), r_ret);
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
scons: *** [godot/poker.os] Error 1
scons: building terminated because of errors.
So I have to ask:
GetTypeInfo
is unable to work with my custom type?godot::Object
the best type to inherit of for your struct
? godot-cpp branch 4.1 has no godot::Resource
type for some reason.Your set_error
function expects a std::string
as parameter but you bound it with ClassDB::bind_method
and Godot can't directly convert between std::string
and godot::String
.
Whenever you bind methods for Godot's ClassBD you must use Godot API types (so any type available to GDScript). If you need to be able to use std::string
consider defining a separate method for that (Not sure if an overload would be handled correctly atm.)
Edit: To answer your 4th question: Resource
still exists but every Godot class has its own header so Resource
would be in <godot_cpp/classes/resource.hpp>
As it turns out the Godot method binder only allows godot::Object
pointers as return and parameter type. If a type inherits godot::RefCounted
the godot::Ref<T>
class can be used as smart pointer that is correctly updated when Godot accesses the object.