summaryrefslogtreecommitdiffstats
path: root/binding_generator.py
diff options
context:
space:
mode:
authorGeorge Marques <george@gmarqu.es>2021-08-19 20:03:11 -0300
committerBastiaan Olij <mux213@gmail.com>2021-09-27 23:08:08 +1000
commita0634cca3f35413baa34f5a40f48345ee8d689ff (patch)
tree77f0837e60423ef837a3bcf5777b19a08d45cdb0 /binding_generator.py
parentb3a4a2cf9360cad559d11eeab8940c1cfc0b32d0 (diff)
downloadredot-cpp-a0634cca3f35413baa34f5a40f48345ee8d689ff.tar.gz
Auto-bind virtual method overrides
Diffstat (limited to 'binding_generator.py')
-rw-r--r--binding_generator.py462
1 files changed, 205 insertions, 257 deletions
diff --git a/binding_generator.py b/binding_generator.py
index 3b45a97..9efd2b7 100644
--- a/binding_generator.py
+++ b/binding_generator.py
@@ -562,23 +562,8 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
# Done in the header because of the template.
continue
- method_signature = ""
- if "return_type" in method:
- method_signature += f'{correct_type(method["return_type"])} '
- else:
- method_signature += "void "
-
- method_signature += f'{class_name}::{method["name"]}('
- if "arguments" in method:
- method_signature += make_function_parameters(
- method["arguments"], include_default=False, for_builtin=True
- )
- method_signature += ")"
- if method["is_const"]:
- method_signature += " const"
- method_signature += " {"
-
- result.append(method_signature)
+ method_signature = make_signature(class_name, method, for_builtin=True)
+ result.append(method_signature + "{")
method_call = "\t"
if "return_type" in method:
@@ -758,6 +743,12 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
if len(fully_used_classes) > 0:
result.append("")
+ if class_name != "Object":
+ result.append("#include <godot_cpp/core/class_db.hpp>")
+ result.append("")
+ result.append("#include <type_traits>")
+ result.append("")
+
result.append("namespace godot {")
result.append("")
@@ -783,114 +774,52 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("\t};")
result.append("")
- has_vararg_method = False
-
if "methods" in class_api:
for method in class_api["methods"]:
if method["is_virtual"]:
- # TODO: See how to bind virtual methods (if they are even needed).
+ # Will be done later.
continue
- method_signature = "\t"
-
vararg = "is_vararg" in method and method["is_vararg"]
+
+ method_signature = "\t"
if vararg:
- has_vararg_method = True
method_signature += "private: "
-
- if "return_value" in method:
- method_signature += correct_type(
- method["return_value"]["type"],
- method["return_value"]["meta"] if "meta" in method["return_value"] else None,
- )
- else:
- method_signature += "void"
-
- if not method_signature.endswith("*"):
- method_signature += " "
-
- method_signature += f'{escape_identifier(method["name"])}'
-
- if vararg or (use_template_get_node and class_name == "Node" and method["name"] == "get_node"):
- method_signature += "_internal"
-
- method_signature += "("
-
- method_arguments = []
- if "arguments" in method:
- method_arguments = method["arguments"]
-
- if not vararg:
- method_signature += make_function_parameters(
- method_arguments, include_default=True, is_vararg=vararg, for_builtin=False
- )
- else:
- method_signature += "const Variant **args, GDNativeInt arg_count"
-
- method_signature += ")"
-
- if method["is_const"]:
- method_signature += " const"
-
- method_signature += ";"
- result.append(method_signature)
+ method_signature += make_signature(
+ class_name, method, for_header=True, use_template_get_node=use_template_get_node
+ )
+ result.append(method_signature + ";")
if vararg:
# Add templated version.
- method_signature = "\tpublic: template<class... Args> "
-
- if "return_value" in method:
- method_signature += correct_type(
- method["return_value"]["type"],
- method["return_value"]["meta"] if "meta" in method["return_value"] else None,
- )
- else:
- method_signature += "void"
-
- if not method_signature.endswith("*"):
- method_signature += " "
-
- method_signature += f'{escape_identifier(method["name"])}'
-
- method_arguments = []
- if "arguments" in method:
- method_arguments = method["arguments"]
-
- method_signature += "("
-
- method_signature += make_function_parameters(method_arguments, include_default=True, is_vararg=vararg)
+ result += make_varargs_template(method)
- method_signature += ")"
-
- if method["is_const"]:
- method_signature += " const"
-
- method_signature += " {"
- result.append(method_signature)
+ # Virtuals now.
+ for method in class_api["methods"]:
+ if not method["is_virtual"]:
+ continue
- args_array = f"\t\tstd::array<Variant, {len(method_arguments)} + sizeof...(Args)> variant_args {{ "
- for argument in method_arguments:
- if argument["type"] == "Variant":
- args_array += argument["name"]
- else:
- args_array += f'Variant({argument["name"]})'
- args_array += ", "
-
- args_array += "Variant(args)... };"
- result.append(args_array)
- result.append(f"\t\tstd::array<const Variant *, {len(method_arguments)} + sizeof...(Args)> call_args;")
- result.append("\t\tfor(size_t i = 0; i < variant_args.size(); i++) {")
- result.append("\t\t\tcall_args[i] = &variant_args[i];")
+ method_signature = "\t"
+ method_signature += make_signature(class_name, method, for_header=True, use_template_get_node=use_template_get_node)
+ result.append(method_signature + ";")
+
+ result.append("protected:")
+ result.append("\ttemplate <class T>")
+ result.append("\tstatic void register_virtuals() {")
+ if class_name != "Object":
+ result.append(f"\t\t{inherits}::register_virtuals<T>();")
+ if "methods" in class_api:
+ for method in class_api["methods"]:
+ if not method["is_virtual"]:
+ continue
+ method_name = escape_identifier(method["name"])
+ result.append(f"\t\tif constexpr (!std::is_same_v<decltype(&{class_name}::{method_name}),decltype(&T::{method_name})>) {{")
+ result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name});")
result.append("\t\t}")
- call_line = "\t\t"
-
- if "return_value" in method and method["return_value"]["type"] != "void":
- call_line += "return "
-
- call_line += f'{escape_identifier(method["name"])}_internal(call_args.data(), variant_args.size());'
- result.append(call_line)
- result.append("\t}")
+ result.append("\t}")
+ result.append("")
+ result.append("public:")
# Special cases.
if class_name == "Object":
@@ -899,7 +828,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("\tstatic T *cast_to(Object *p_object);")
elif use_template_get_node and class_name == "Node":
result.append("\ttemplate<class T>")
- result.append("\tT *get_node(const NodePath &p_path) { return Object::cast_to<T>(get_node_internal(p_path)); }")
+ result.append("\tT *get_node(const NodePath &p_path) const { return Object::cast_to<T>(get_node_internal(p_path)); }")
# Constructor.
result.append("")
@@ -943,49 +872,14 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
if "methods" in class_api:
for method in class_api["methods"]:
if method["is_virtual"]:
- # TODO: See how to bind virtual methods (if they are even needed).
+ # Will be done later
continue
vararg = "is_vararg" in method and method["is_vararg"]
# Method signature.
- method_signature = ""
- if "return_value" in method:
- method_signature += correct_type(
- method["return_value"]["type"],
- method["return_value"]["meta"] if "meta" in method["return_value"] else None,
- )
- else:
- method_signature += "void"
-
- if not method_signature.endswith("*"):
- method_signature += " "
-
- method_signature += f'{class_name}::{escape_identifier(method["name"])}'
-
- if vararg or (use_template_get_node and class_name == "Node" and method["name"] == "get_node"):
- method_signature += "_internal"
-
- method_signature += "("
-
- method_arguments = []
- if "arguments" in method:
- method_arguments = method["arguments"]
-
- if not vararg:
- method_signature += make_function_parameters(
- method_arguments, include_default=False, is_vararg=vararg, for_builtin=False
- )
- else:
- method_signature += "const Variant **args, GDNativeInt arg_count"
-
- method_signature += ")"
-
- if method["is_const"]:
- method_signature += " const"
-
- method_signature += " {"
- result.append(method_signature)
+ method_signature = make_signature(class_name, method, use_template_get_node=use_template_get_node)
+ result.append(method_signature + " {")
# Method body.
result.append(
@@ -1044,6 +938,22 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append("}")
result.append("")
+ # Virtuals now.
+ for method in class_api["methods"]:
+ if not method["is_virtual"]:
+ continue
+
+ method_signature = make_signature(class_name, method, use_template_get_node=use_template_get_node)
+ method_signature += " {"
+ if "return_value" in method and correct_type(method["return_value"]["type"]) != "void":
+ result.append(method_signature)
+ result.append(f'\treturn {get_default_value_for_type(method["return_value"]["type"])};')
+ result.append("}")
+ else:
+ method_signature += "}"
+ result.append(method_signature)
+ result.append("")
+
# Constructor.
result.append(f"{class_name}::{class_name}() : {inherits}(godot::internal::empty_constructor()) {{")
result.append(
@@ -1138,85 +1048,12 @@ def generate_utility_functions(api, output_dir):
vararg = "is_vararg" in function and function["is_vararg"]
function_signature = "\t"
- if vararg:
- function_signature += "private: "
- function_signature += "static "
-
- if "return_type" in function:
- if not vararg:
- function_signature += f'{correct_type(function["return_type"])} '
- else:
- function_signature += "Variant "
- else:
- function_signature += "void "
-
- function_signature += f'{escape_identifier(function["name"])}'
-
- if vararg:
- function_signature += "_internal"
-
- function_signature += "("
-
- function_arguments = []
- if "arguments" in function:
- function_arguments = function["arguments"]
-
- if not vararg:
- function_signature += make_function_parameters(function_arguments, include_default=False)
- else:
- function_signature += "const Variant **args, GDNativeInt arg_count"
- function_signature += ");"
-
- header.append(function_signature)
+ function_signature += make_signature("UtilityFunctions", function, for_header=True, static=True)
+ header.append(function_signature + ";")
if vararg:
# Add templated version.
- method_signature = "\tpublic: template<class... Args> static "
-
- if "return_type" in function:
- method_signature += correct_type(function["return_type"])
- else:
- method_signature += "void"
-
- if not method_signature.endswith("*"):
- method_signature += " "
-
- method_signature += f'{escape_identifier(function["name"])}'
-
- method_arguments = []
- if "arguments" in function:
- method_arguments = function["arguments"]
-
- method_signature += "("
- method_signature += make_function_parameters(method_arguments, include_default=True, is_vararg=vararg)
- method_signature += ")"
-
- method_signature += " {"
- header.append(method_signature)
-
- args_array = f"\t\tstd::array<Variant, {len(method_arguments)} + sizeof...(Args)> variant_args {{ "
- for argument in method_arguments:
- if argument["type"] == "Variant":
- args_array += argument["name"]
- else:
- args_array += f'Variant({argument["name"]})'
- args_array += ", "
-
- args_array += "Variant(args)... };"
- header.append(args_array)
- header.append(f"\t\tstd::array<const Variant *, {len(method_arguments)} + sizeof...(Args)> call_args;")
- header.append("\t\tfor(size_t i = 0; i < variant_args.size(); i++) {")
- header.append("\t\t\tcall_args[i] = &variant_args[i];")
- header.append("\t\t}")
-
- call_line = "\t\t"
-
- if "return_type" in function and function["return_type"] != "void":
- call_line += "return "
-
- call_line += f'{escape_identifier(function["name"])}_internal(call_args.data(), variant_args.size());'
- header.append(call_line)
- header.append("\t}")
+ header += make_varargs_template(function, static=True)
header.append("};")
header.append("")
@@ -1244,34 +1081,8 @@ def generate_utility_functions(api, output_dir):
for function in api["utility_functions"]:
vararg = "is_vararg" in function and function["is_vararg"]
- function_signature = ""
- if "return_type" in function:
- if not vararg:
- function_signature += f'{correct_type(function["return_type"])}'
- else:
- function_signature += "Variant"
- else:
- function_signature += "void"
-
- if not function_signature.endswith("*"):
- function_signature += " "
-
- function_signature += f'UtilityFunctions::{escape_identifier(function["name"])}'
- if vararg:
- function_signature += "_internal"
- function_signature += "("
-
- function_arguments = []
- if "arguments" in function:
- function_arguments = function["arguments"]
-
- if not vararg:
- function_signature += make_function_parameters(function_arguments, include_default=False)
- else:
- function_signature += "const Variant **args, GDNativeInt arg_count"
- function_signature += ") {"
-
- source.append(function_signature)
+ function_signature = make_signature("UtilityFunctions", function)
+ source.append(function_signature + " {")
# Function body.
@@ -1340,14 +1151,20 @@ def camel_to_snake(name):
def make_function_parameters(parameters, include_default=False, for_builtin=False, is_vararg=False):
signature = []
- for par in parameters:
+ for index, par in enumerate(parameters):
parameter = type_for_parameter(par["type"], par["meta"] if "meta" in par else None)
- parameter += escape_identifier(par["name"])
+ parameter_name = escape_identifier(par["name"])
+ if len(parameter_name) == 0:
+ parameter_name = "arg_" + str(index + 1)
+ parameter += parameter_name
if include_default and "default_value" in par and (not for_builtin or par["type"] != "Variant"):
parameter += " = "
if is_enum(par["type"]):
- parameter += f'({correct_type(par["type"])})'
+ parameter_type = correct_type(par["type"])
+ if parameter_type == "void":
+ parameter_type = "Variant"
+ parameter += f'({parameter_type})'
parameter += correct_default_value(par["default_value"], par["type"])
signature.append(parameter)
@@ -1358,7 +1175,9 @@ def make_function_parameters(parameters, include_default=False, for_builtin=Fals
def type_for_parameter(type_name, meta=None):
- if is_pod_type(type_name) and type_name != "Nil" or is_enum(type_name):
+ if type_name == "void":
+ return "Variant "
+ elif is_pod_type(type_name) and type_name != "Nil" or is_enum(type_name):
return f"{correct_type(type_name, meta)} "
elif is_variant(type_name) or is_refcounted(type_name):
return f"const {correct_type(type_name)} &"
@@ -1395,6 +1214,135 @@ def get_encoded_arg(arg_name, type_name, type_meta):
return (result, name)
+def make_signature(class_name, function_data, for_header=False, use_template_get_node=True, for_builtin=False, static=False):
+ function_signature = ""
+
+ is_vararg = "is_vararg" in function_data and function_data["is_vararg"]
+
+ if for_header:
+ if "is_virtual" in function_data and function_data["is_virtual"]:
+ function_signature += "virtual "
+
+ if is_vararg:
+ function_signature += "private: "
+
+ if static:
+ function_signature += "static "
+
+ return_type = "void"
+ return_meta = None
+ if "return_type" in function_data:
+ return_type = correct_type(function_data["return_type"])
+ elif "return_value" in function_data:
+ return_type = function_data["return_value"]["type"]
+ return_meta = function_data["return_value"]["meta"] if "meta" in function_data["return_value"] else None
+
+ function_signature += correct_type(
+ return_type,
+ return_meta,
+ )
+
+ if not function_signature.endswith("*"):
+ function_signature += " "
+
+ if not for_header:
+ function_signature += f"{class_name}::"
+
+ function_signature += escape_identifier(function_data["name"])
+
+ if is_vararg or (
+ not for_builtin and use_template_get_node and class_name == "Node" and function_data["name"] == "get_node"
+ ):
+ function_signature += "_internal"
+
+ function_signature += "("
+
+ arguments = function_data["arguments"] if "arguments" in function_data else []
+
+ if not is_vararg:
+ function_signature += make_function_parameters(arguments, for_header, for_builtin, is_vararg)
+ else:
+ function_signature += "const Variant **args, GDNativeInt arg_count"
+
+ function_signature += ")"
+
+ if "is_const" in function_data and function_data["is_const"]:
+ function_signature += " const"
+
+ return function_signature
+
+
+def make_varargs_template(function_data, static=False):
+ result = []
+
+ function_signature = "\tpublic: template<class... Args> "
+
+ if static:
+ function_signature += "static "
+
+ return_type = "void"
+ return_meta = None
+ if "return_type" in function_data:
+ return_type = correct_type(function_data["return_type"])
+ elif "return_value" in function_data:
+ return_type = function_data["return_value"]["type"]
+ return_meta = function_data["return_value"]["meta"] if "meta" in function_data["return_value"] else None
+
+ function_signature += correct_type(
+ return_type,
+ return_meta,
+ )
+
+ if not function_signature.endswith("*"):
+ function_signature += " "
+
+ function_signature += f'{escape_identifier(function_data["name"])}'
+
+ method_arguments = []
+ if "arguments" in function_data:
+ method_arguments = function_data["arguments"]
+
+ function_signature += "("
+
+ is_vararg = "is_vararg" in function_data and function_data["is_vararg"]
+
+ function_signature += make_function_parameters(method_arguments, include_default=True, is_vararg=is_vararg)
+
+ function_signature += ")"
+
+ if "is_const" in function_data and function_data["is_const"]:
+ function_signature += " const"
+
+ function_signature += " {"
+ result.append(function_signature)
+
+ args_array = f"\t\tstd::array<Variant, {len(method_arguments)} + sizeof...(Args)> variant_args {{ "
+ for argument in method_arguments:
+ if argument["type"] == "Variant":
+ args_array += argument["name"]
+ else:
+ args_array += f'Variant({argument["name"]})'
+ args_array += ", "
+
+ args_array += "Variant(args)... };"
+ result.append(args_array)
+ result.append(f"\t\tstd::array<const Variant *, {len(method_arguments)} + sizeof...(Args)> call_args;")
+ result.append("\t\tfor(size_t i = 0; i < variant_args.size(); i++) {")
+ result.append("\t\t\tcall_args[i] = &variant_args[i];")
+ result.append("\t\t}")
+
+ call_line = "\t\t"
+
+ if "return_value" in function_data and function_data["return_value"]["type"] != "void":
+ call_line += "return "
+
+ call_line += f'{escape_identifier(function_data["name"])}_internal(call_args.data(), variant_args.size());'
+ result.append(call_line)
+ result.append("\t}")
+
+ return result
+
+
# Engine idiosyncrasies.