Skip to content
Merged
66 changes: 55 additions & 11 deletions binding_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision=
generate_utility_functions(api, target_dir)


CLASS_ALIASES = {
"ClassDB": "ClassDBSingleton",
}

builtin_classes = []

# Key is class name, value is boolean where True means the class is refcounted.
Expand Down Expand Up @@ -1079,19 +1083,19 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
# First create map of classes and singletons.
for class_api in api["classes"]:
# Generate code for the ClassDB singleton under a different name.
if class_api["name"] == "ClassDB":
class_api["name"] = "ClassDBSingleton"
class_api["alias_for"] = "ClassDB"
if class_api["name"] in CLASS_ALIASES:
class_api["alias_for"] = class_api["name"]
class_api["name"] = CLASS_ALIASES[class_api["alias_for"]]
engine_classes[class_api["name"]] = class_api["is_refcounted"]
for native_struct in api["native_structures"]:
engine_classes[native_struct["name"]] = False
native_structures.append(native_struct["name"])

for singleton in api["singletons"]:
# Generate code for the ClassDB singleton under a different name.
if singleton["name"] == "ClassDB":
singleton["name"] = "ClassDBSingleton"
singleton["alias_for"] = "ClassDB"
if singleton["name"] in CLASS_ALIASES:
singleton["alias_for"] = singleton["name"]
singleton["name"] = CLASS_ALIASES[singleton["name"]]
singletons.append(singleton["name"])

for class_api in api["classes"]:
Expand Down Expand Up @@ -1294,6 +1298,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("#include <type_traits>")
result.append("")

if class_name == "ClassDBSingleton":
result.append("#include <godot_cpp/core/binder_common.hpp>")
result.append("")

result.append("namespace godot {")
result.append("")

Expand Down Expand Up @@ -1448,6 +1456,19 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us

if class_name == "ClassDBSingleton":
result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\")

if "enums" in class_api:
for enum_api in class_api["enums"]:
if enum_api["is_bitfield"]:
result.append(f'\tenum {enum_api["name"]} : uint64_t {{ \\')
else:
result.append(f'\tenum {enum_api["name"]} {{ \\')

for value in enum_api["values"]:
result.append(f'\t\t{value["name"]} = {value["value"]}, \\')
result.append("\t}; \\")
result.append("\t \\")

for method in class_api["methods"]:
# ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
if vararg:
Expand All @@ -1456,12 +1477,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
continue

method_signature = "\tstatic "
return_type = None
if "return_type" in method:
method_signature += f'{correct_type(method["return_type"])} '
return_type = correct_type(method["return_type"].replace("ClassDBSingleton", "ClassDB"), None, False)
elif "return_value" in method:
method_signature += (
correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " "
return_type = correct_type(
method["return_value"]["type"].replace("ClassDBSingleton", "ClassDB"),
method["return_value"].get("meta", None),
False,
)
if return_type is not None:
method_signature += return_type + " "
else:
method_signature += "void "

Expand All @@ -1480,8 +1506,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(method_signature)

method_body = "\t\t"
if "return_type" in method or "return_value" in method:
if return_type is not None:
method_body += "return "
if "alias_for" in class_api and return_type.startswith(class_api["alias_for"] + "::"):
method_body += f"({return_type})"
method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}('
method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
method_body += "); \\"
Expand All @@ -1491,6 +1519,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("\t;")
result.append("")

result.append("#define CLASSDB_SINGLETON_VARIANT_CAST \\")

if "enums" in class_api:
for enum_api in class_api["enums"]:
if enum_api["is_bitfield"]:
result.append(f'\tVARIANT_BITFIELD_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
else:
result.append(f'\tVARIANT_ENUM_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')

result.append("\t;")
result.append("")

result.append(f"#endif // ! {header_guard}")

return "\n".join(result)
Expand Down Expand Up @@ -2285,7 +2325,7 @@ def correct_typed_array(type_name):
return type_name


def correct_type(type_name, meta=None):
def correct_type(type_name, meta=None, use_alias=True):
type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
if meta != None:
if "int" in meta:
Expand All @@ -2301,11 +2341,15 @@ def correct_type(type_name, meta=None):
if is_enum(type_name):
if is_bitfield(type_name):
base_class = get_enum_class(type_name)
if use_alias and base_class in CLASS_ALIASES:
base_class = CLASS_ALIASES[base_class]
if base_class == "GlobalConstants":
return f"BitField<{get_enum_name(type_name)}>"
return f"BitField<{base_class}::{get_enum_name(type_name)}>"
else:
base_class = get_enum_class(type_name)
if use_alias and base_class in CLASS_ALIASES:
base_class = CLASS_ALIASES[base_class]
if base_class == "GlobalConstants":
return f"{get_enum_name(type_name)}"
return f"{base_class}::{get_enum_name(type_name)}"
Expand Down
27 changes: 19 additions & 8 deletions include/godot_cpp/classes/wrapped.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,15 @@ class Wrapped {
uint32_t plist_size = 0;

void _postinitialize();
virtual void _notificationv(int32_t p_what) {}

Wrapped(const StringName p_godot_class);
Wrapped(GodotObject *p_godot_object);
virtual ~Wrapped() {}

public:
static StringName &get_class_static() {
static StringName string_name = StringName("Wrapped");
static const StringName &get_class_static() {
static const StringName string_name = StringName("Wrapped");
return string_name;
}

Expand Down Expand Up @@ -171,6 +172,7 @@ protected:
\
public: \
typedef m_class self_type; \
typedef m_inherits parent_type; \
\
static void initialize_class() { \
static bool initialized = false; \
Expand All @@ -185,12 +187,12 @@ public:
initialized = true; \
} \
\
static ::godot::StringName &get_class_static() { \
static ::godot::StringName string_name = ::godot::StringName(#m_class); \
static const ::godot::StringName &get_class_static() { \
static const ::godot::StringName string_name = ::godot::StringName(#m_class); \
return string_name; \
} \
\
static ::godot::StringName &get_parent_class_static() { \
static const ::godot::StringName &get_parent_class_static() { \
return m_inherits::get_class_static(); \
} \
\
Expand Down Expand Up @@ -327,13 +329,19 @@ public:
_gde_binding_reference_callback, \
}; \
\
protected: \
virtual void _notificationv(int32_t p_what) override { \
m_class::notification_bind(this, p_what); \
} \
\
private:

// Don't use this for your classes, use GDCLASS() instead.
#define GDEXTENSION_CLASS_ALIAS(m_class, m_alias_for, m_inherits) /******************************************************************************************************************/ \
private: \
inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \
void operator=(const m_class &p_rval) {} \
friend class ::godot::ClassDB; \
\
protected: \
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
Expand All @@ -343,6 +351,8 @@ protected:
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
\
static void _bind_methods() {} \
\
static void (*_get_bind_methods())() { \
return nullptr; \
} \
Expand Down Expand Up @@ -385,15 +395,16 @@ protected:
\
public: \
typedef m_class self_type; \
typedef m_inherits parent_type; \
\
static void initialize_class() {} \
\
static ::godot::StringName &get_class_static() { \
static ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \
static const ::godot::StringName &get_class_static() { \
static const ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \
return string_name; \
} \
\
static ::godot::StringName &get_parent_class_static() { \
static const ::godot::StringName &get_parent_class_static() { \
return m_inherits::get_class_static(); \
} \
\
Expand Down
4 changes: 4 additions & 0 deletions include/godot_cpp/core/class_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ class ClassDB {
template <typename T, bool is_abstract>
void ClassDB::_register_class(bool p_virtual) {
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
static_assert(!FunctionsAreSame<T::self_type::_bind_methods, T::parent_type::_bind_methods>::value, "Class must declare 'static void _bind_methods'.");
static_assert(!std::is_abstract_v<T> || is_abstract, "Class is abstract, please use GDREGISTER_ABSTRACT_CLASS.");
instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;

// Register this class within our plugin
Expand Down Expand Up @@ -290,4 +292,6 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p

} // namespace godot

CLASSDB_SINGLETON_VARIANT_CAST;

#endif // GODOT_CLASS_DB_HPP
Loading