added type tags and safe object casting

pull/155/head
karroffel 2018-04-04 21:30:28 +02:00
parent 37abbe0c09
commit dabc96ebd9
8 changed files with 196 additions and 27 deletions

View File

@ -34,6 +34,9 @@ def generate_bindings(path):
icall_source_file = open("src/gen/__icalls.cpp", "w+")
icall_source_file.write(generate_icall_implementation(icalls))
register_types_file = open("src/gen/__register_types.cpp", "w+")
register_types_file.write(generate_type_registry(classes))
def is_reference_type(t):
for c in classes:
@ -79,6 +82,8 @@ def generate_class_header(used_classes, c):
# so don't include it here because it's not needed
if class_name != "Object" and class_name != "Reference":
source.append("#include <core/Ref.hpp>")
else:
source.append("#include <TagDB.hpp>")
included = []
@ -101,7 +106,6 @@ def generate_class_header(used_classes, c):
source.append("#include \"" + strip_name(c["base_class"]) + ".hpp\"")
source.append("namespace godot {")
source.append("")
@ -118,17 +122,12 @@ def generate_class_header(used_classes, c):
vararg_templates = ""
# generate the class definition here
source.append("class " + class_name + ("" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {")
source.append("class " + class_name + (" : public _Wrapped" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {")
if c["base_class"] == "":
# this is Object
source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };")
source.append("private:")
source.append("")
source.append("public:")
source.append("\tgodot_object *_owner;")
source.append("")
# TODO decide what to do about virtual methods
# source.append("static void _register_methods();")
# source.append("")
if c["singleton"]:
source.append("\tstatic " + class_name + " *_singleton;")
@ -140,6 +139,10 @@ def generate_class_header(used_classes, c):
source.append("public:")
source.append("")
source.append("\tstatic void *___get_type_tag();")
source.append("\tstatic void *___get_base_type_tag();")
if c["singleton"]:
source.append("\tstatic inline " + class_name + " *get_singleton()")
source.append("\t{")
@ -181,6 +184,14 @@ def generate_class_header(used_classes, c):
source.append("\n\t// methods")
if class_name == "Object":
source.append("#ifndef GODOT_CPP_NO_OBJECT_CAST")
source.append("\ttemplate<class T>")
source.append("\tstatic T *cast_to(const Object *obj);")
source.append("#endif")
source.append("")
for method in c["methods"]:
method_signature = ""
@ -264,11 +275,10 @@ def generate_class_header(used_classes, c):
source.append("")
source.append("}")
source.append("")
source.append("#endif")
@ -315,6 +325,21 @@ def generate_class_implementation(icalls, used_classes, c):
source.append("")
source.append("")
source.append("void *" + class_name + "::___get_type_tag()")
source.append("{")
source.append("\treturn (void *) &" + class_name + "::___get_type_tag;")
source.append("}")
source.append("")
source.append("void *" + class_name + "::___get_base_type_tag()")
source.append("{")
if c["base_class"] != "":
source.append("\treturn (void *) &" + strip_name(c["base_class"]) + "::___get_type_tag;")
else:
source.append("\treturn (void *) nullptr;")
source.append("}")
source.append("")
if c["singleton"]:
source.append("" + class_name + " *" + class_name + "::_singleton = NULL;")
source.append("")
@ -640,8 +665,34 @@ def generate_icall_implementation(icalls):
def generate_type_registry(classes):
source = []
source.append("#include \"TagDB.hpp\"")
source.append("")
for c in classes:
source.append("#include <" + strip_name(c["name"]) + ".hpp>")
source.append("")
source.append("")
source.append("namespace godot {")
source.append("void ___register_types()")
source.append("{")
for c in classes:
class_name = strip_name(c["name"])
source.append("\tgodot::_TagDB::register_global_type(\"" + class_name + "\", " + class_name + "::___get_type_tag(), " + class_name + "::___get_base_type_tag());")
source.append("}")
source.append("")
source.append("}")
return "\n".join(source)

View File

@ -21,5 +21,7 @@
#include "Vector2.hpp"
#include "Vector3.hpp"
#include "Wrapped.hpp"
#endif // CORETYPES_H

View File

@ -11,6 +11,7 @@
#include "CoreTypes.hpp"
#include "Variant.hpp"
#include "Ref.hpp"
#include "TagDB.hpp"
#include "Object.hpp"
@ -23,7 +24,7 @@ namespace godot {
template<class T>
T *as(Object *obj)
T *as(const Object *obj)
{
return (T *) godot::nativescript_api->godot_nativescript_get_userdata(obj->_owner);
}
@ -37,9 +38,12 @@ T *get_wrapper(godot_object *obj)
#define GODOT_CLASS(Name, Base) \
public: inline static const char *___get_type_name() { return static_cast<const char *>(#Name); } \
enum { ___CLASS_IS_SCRIPT = 1, }; \
inline static Name *_new() { godot::NativeScript *script = godot::NativeScript::_new(); script->set_library(godot::get_wrapper<godot::GDNativeLibrary>((godot_object *) godot::gdnlib)); script->set_class_name(#Name); Name *instance = godot::as<Name>(script->new_()); return instance; } \
inline static const char *___get_base_type_name() { return Base::___get_class_name(); } \
inline static Object *___get_from_variant(godot::Variant a) { return (godot::Object *) godot::as<Name>(godot::Object::___get_from_variant(a)); } \
inline static void *___get_type_tag() { return (void *) &Name::___get_type_tag; } \
inline static void *___get_base_type_tag() { return (void *) &Base::___get_type_tag; } \
private:
#define GODOT_SUBCLASS(Name, Base) \
@ -83,6 +87,7 @@ void *_godot_class_instance_func(godot_object *p, void *method_data)
{
T *d = new T();
d->_owner = p;
d->_type_tag = T::___get_type_tag();
d->_init();
return d;
}
@ -104,8 +109,10 @@ void register_class()
godot_instance_destroy_func destroy = {};
destroy.destroy_func = _godot_class_destroy_func<T>;
_TagDB::register_type(T::___get_type_tag(), T::___get_base_type_tag());
godot::nativescript_api->godot_nativescript_register_class(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_base_type_name(), create, destroy);
godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_type_tag());
T::_register_methods();
}
@ -118,8 +125,10 @@ void register_tool_class()
godot_instance_destroy_func destroy = {};
destroy.destroy_func = _godot_class_destroy_func<T>;
_TagDB::register_type(T::___get_type_tag(), T::___get_base_type_tag());
godot::nativescript_api->godot_nativescript_register_tool_class(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_base_type_name(), create, destroy);
godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_type_tag());
T::_register_methods();
}
@ -478,6 +487,23 @@ void register_signal(String name, Args... varargs)
register_signal<T>(name, Dictionary::make(varargs...));
}
#ifndef GODOT_CPP_NO_OBJECT_CAST
template<class T>
T *Object::cast_to(const Object *obj)
{
if (godot::_TagDB::is_type_compatible(T::___get_type_tag(), obj->_type_tag)) {
return (T::___CLASS_IS_SCRIPT) ? godot::as<T>(obj) : (T *) obj;
} else {
return nullptr;
}
}
#endif
}
#endif // GODOT_H

View File

@ -107,7 +107,7 @@ public:
void operator=(const Variant &p_variant) {
// TODO We need a safe cast
Reference *refb = (Reference *) Object::___get_from_variant(p_variant);
Reference *refb = (Reference *) T::___get_from_variant(p_variant);
if (!refb) {
unref();
return;
@ -156,7 +156,7 @@ public:
reference = nullptr;
// TODO We need a safe cast
Reference *refb = (Reference *) Object::___get_from_variant(p_variant);
Reference *refb = (Reference *) T::___get_from_variant(p_variant);
if (!refb) {
unref();
return;

17
include/core/TagDB.hpp Normal file
View File

@ -0,0 +1,17 @@
#ifndef TAGDB_HPP
#define TAGDB_HPP
namespace godot {
namespace _TagDB {
void register_type(const void *type_tag, const void *base_type_tag);
void register_global_type(const char *name, const void *type_tag, const void *base_type_tag);
bool is_type_compatible(const void *type_tag, const void *base_type_tag);
}
}
#endif // TAGDB_HPP

16
include/core/Wrapped.hpp Normal file
View File

@ -0,0 +1,16 @@
#ifndef WRAPPED_HPP
#define WRAPPED_HPP
#include <gdnative/gdnative.h>
namespace godot {
class _Wrapped {
public:
godot_object *_owner;
const void *_type_tag;
};
}
#endif // WRAPPED_HPP

View File

@ -2,18 +2,18 @@
#include "String.hpp"
static GDCALLINGCONV void *wrapper_create(void *data, godot_object *instance)
#include "Wrapped.hpp"
static GDCALLINGCONV void *wrapper_create(void *data, const void *type_tag, godot_object *instance)
{
void *wrapper_memory = godot::api->godot_alloc(sizeof(instance));
godot::_Wrapped *wrapper_memory = (godot::_Wrapped *) godot::api->godot_alloc(sizeof(godot::_Wrapped));
if (!wrapper_memory)
return NULL;
wrapper_memory->_owner = instance;
wrapper_memory->_type_tag = type_tag;
godot_object **wrapper = (godot_object **) wrapper_memory;
*wrapper = instance;
return wrapper;
return (void *) wrapper_memory;
}
static GDCALLINGCONV void wrapper_destroy(void *data, void *wrapper)
@ -71,6 +71,8 @@ void Godot::print_error(const String& description, const String& function, const
if (c_file != nullptr) godot::api->godot_free(c_file);
}
void ___register_types();
void Godot::gdnative_init(godot_gdnative_init_options *options)
{
godot::api = options->api_struct;
@ -92,10 +94,11 @@ void Godot::gdnative_init(godot_gdnative_init_options *options)
extension = extension->next;
}
} break;
default: {
}break;
};
};
default: break;
}
}
___register_types();
}
void Godot::gdnative_terminate(godot_gdnative_terminate_options *options)

54
src/core/TagDB.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "TagDB.hpp"
#include <unordered_map>
#include <GodotGlobal.hpp>
namespace godot {
namespace _TagDB {
std::unordered_map<const void *, const void *> parent_to;
void register_type(const void *type_tag, const void *base_type_tag)
{
parent_to[type_tag] = base_type_tag;
}
void register_global_type(const char *name, const void *type_tag, const void *base_type_tag)
{
godot::nativescript_1_1_api->godot_nativescript_set_global_type_tag(godot::_RegisterState::language_index, name, type_tag);
register_type(type_tag, base_type_tag);
}
bool is_type_compatible(const void *type_tag, const void *base_type_tag)
{
if (type_tag == nullptr || base_type_tag == nullptr)
return false;
if (type_tag == base_type_tag)
return true;
const void *tag = type_tag;
while (true) {
if (tag == base_type_tag) {
return true;
}
if (tag == nullptr) {
return false;
}
tag = parent_to[tag];
}
return false;
}
}
}