Skip to content

Commit

Permalink
Merge pull request #1205 from dsnopek/4.1-cherrypicks-1
Browse files Browse the repository at this point in the history
Cherry-picks for the godot-cpp 4.1 branch - 1st batch
  • Loading branch information
dsnopek authored Aug 11, 2023
2 parents 1009da4 + 4fb9af7 commit 28494f0
Show file tree
Hide file tree
Showing 17 changed files with 340 additions and 249 deletions.
39 changes: 33 additions & 6 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@ def normalize_path(val):
return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val)


def validate_api_file(key, val, env):
def validate_file(key, val, env):
if not os.path.isfile(normalize_path(val)):
raise UserError("GDExtension API file ('%s') does not exist: %s" % (key, val))
raise UserError("'%s' is not a file: %s" % (key, val))


def validate_gdextension_dir(key, val, env):
def validate_dir(key, val, env):
if not os.path.isdir(normalize_path(val)):
raise UserError("GDExtension directory ('%s') does not exist: %s" % (key, val))
raise UserError("'%s' is not a directory: %s" % (key, val))


def validate_parent_dir(key, val, env):
if not os.path.isdir(normalize_path(os.path.dirname(val))):
raise UserError("'%s' is not a directory: %s" % (key, os.path.dirname(val)))


def get_gdextension_dir(env):
Expand Down Expand Up @@ -115,15 +120,15 @@ opts.Add(
key="gdextension_dir",
help="Path to a custom directory containing GDExtension interface header and API JSON file",
default=env.get("gdextension_dir", None),
validator=validate_gdextension_dir,
validator=validate_dir,
)
)
opts.Add(
PathVariable(
key="custom_api_file",
help="Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`)",
default=env.get("custom_api_file", None),
validator=validate_api_file,
validator=validate_file,
)
)
opts.Add(
Expand Down Expand Up @@ -151,6 +156,23 @@ opts.Add(
)
)

# compiledb
opts.Add(
BoolVariable(
key="compiledb",
help="Generate compilation DB (`compile_commands.json`) for external tools",
default=env.get("compiledb", False),
)
)
opts.Add(
PathVariable(
key="compiledb_file",
help="Path to a custom `compile_commands.json` file",
default=env.get("compiledb_file", "compile_commands.json"),
validator=validate_parent_dir,
)
)

# Add platform options
tools = {}
for pl in platforms:
Expand Down Expand Up @@ -242,6 +264,11 @@ else:
if env["precision"] == "double":
env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])

# compile_commands.json
if env.get("compiledb", False):
env.Tool("compilation_db")
env.Alias("compiledb", env.CompilationDatabase(normalize_path(env["compiledb_file"])))

# Generate bindings
env.Append(BUILDERS={"GenerateBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)})

Expand Down
21 changes: 12 additions & 9 deletions binding_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1453,16 +1453,22 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us

if is_singleton:
result.append(f"{class_name} *{class_name}::get_singleton() {{")
result.append(f"\tconst StringName _gde_class_name = {class_name}::get_class_static();")
# We assume multi-threaded access is OK because each assignment will assign the same value every time
result.append(f"\tstatic {class_name} *singleton = nullptr;")
result.append("\tif (unlikely(singleton == nullptr)) {")
result.append(
"\tstatic GDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton(_gde_class_name._native_ptr());"
f"\t\tGDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton({class_name}::get_class_static()._native_ptr());"
)
result.append("#ifdef DEBUG_ENABLED")
result.append("\tERR_FAIL_COND_V(singleton_obj == nullptr, nullptr);")
result.append("\t\tERR_FAIL_COND_V(singleton_obj == nullptr, nullptr);")
result.append("#endif // DEBUG_ENABLED")
result.append(
f"\tstatic {class_name} *singleton = reinterpret_cast<{class_name} *>(internal::gdextension_interface_object_get_instance_binding(singleton_obj, internal::token, &{class_name}::_gde_binding_callbacks));"
f"\t\tsingleton = reinterpret_cast<{class_name} *>(internal::gdextension_interface_object_get_instance_binding(singleton_obj, internal::token, &{class_name}::_gde_binding_callbacks));"
)
result.append("#ifdef DEBUG_ENABLED")
result.append("\t\tERR_FAIL_COND_V(singleton == nullptr, nullptr);")
result.append("#endif // DEBUG_ENABLED")
result.append("\t}")
result.append("\treturn singleton;")
result.append("}")
result.append("")
Expand All @@ -1480,10 +1486,8 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append(method_signature + " {")

# Method body.
result.append(f"\tconst StringName _gde_class_name = {class_name}::get_class_static();")
result.append(f'\tconst StringName _gde_method_name = "{method["name"]}";')
result.append(
f'\tstatic GDExtensionMethodBindPtr _gde_method_bind = internal::gdextension_interface_classdb_get_method_bind(_gde_class_name._native_ptr(), _gde_method_name._native_ptr(), {method["hash"]});'
f'\tstatic GDExtensionMethodBindPtr _gde_method_bind = internal::gdextension_interface_classdb_get_method_bind({class_name}::get_class_static()._native_ptr(), StringName("{method["name"]}")._native_ptr(), {method["hash"]});'
)
method_call = "\t"
has_return = "return_value" in method and method["return_value"]["type"] != "void"
Expand Down Expand Up @@ -1773,9 +1777,8 @@ def generate_utility_functions(api, output_dir):

# Function body.

source.append(f'\tconst StringName _gde_function_name = "{function["name"]}";')
source.append(
f'\tstatic GDExtensionPtrUtilityFunction _gde_function = internal::gdextension_interface_variant_get_ptr_utility_function(_gde_function_name._native_ptr(), {function["hash"]});'
f'\tstatic GDExtensionPtrUtilityFunction _gde_function = internal::gdextension_interface_variant_get_ptr_utility_function(StringName("{function["name"]}")._native_ptr(), {function["hash"]});'
)
has_return = "return_type" in function and function["return_type"] != "void"
if has_return:
Expand Down
8 changes: 7 additions & 1 deletion include/godot_cpp/templates/cowdata.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
#define GODOT_COWDATA_HPP

#include <godot_cpp/classes/global_constants.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/error_macros.hpp>
#include <godot_cpp/core/math.hpp>
#include <godot_cpp/core/memory.hpp>
#include <godot_cpp/templates/safe_refcount.hpp>

#include <cstring>
#include <new>

namespace godot {

Expand All @@ -48,6 +48,9 @@ class Vector;
template <class T, class V>
class VMap;

template <class T>
class CharStringT;

// Silence a false positive warning (see GH-52119).
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
Expand All @@ -62,6 +65,9 @@ class CowData {
template <class TV, class VV>
friend class VMap;

template <class TS>
friend class CharStringT;

private:
mutable T *_ptr = nullptr;

Expand Down
125 changes: 71 additions & 54 deletions include/godot_cpp/variant/char_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,83 +31,100 @@
#ifndef GODOT_CHAR_STRING_HPP
#define GODOT_CHAR_STRING_HPP

#include <godot_cpp/templates/cowdata.hpp>

#include <cstddef>
#include <cstdint>

namespace godot {

class CharString {
friend class String;

const char *_data = nullptr;
int _length = 0;

CharString(const char *str, int length);

public:
int length() const;
const char *get_data() const;

CharString(CharString &&p_str);
void operator=(CharString &&p_str);
CharString() {}
~CharString();
};
template <class T>
class CharStringT;

class Char16String {
friend class String;
template <class T>
class CharProxy {
template <class TS>
friend class CharStringT;

const char16_t *_data = nullptr;
int _length = 0;
const int _index;
CowData<T> &_cowdata;
static inline const T _null = 0;

Char16String(const char16_t *str, int length);
_FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &p_cowdata) :
_index(p_index),
_cowdata(p_cowdata) {}

public:
int length() const;
const char16_t *get_data() const;

Char16String(Char16String &&p_str);
void operator=(Char16String &&p_str);
Char16String() {}
~Char16String();
};
_FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) :
_index(p_other._index),
_cowdata(p_other._cowdata) {}

class Char32String {
friend class String;
_FORCE_INLINE_ operator T() const {
if (unlikely(_index == _cowdata.size())) {
return _null;
}

const char32_t *_data = nullptr;
int _length = 0;
return _cowdata.get(_index);
}

Char32String(const char32_t *str, int length);
_FORCE_INLINE_ const T *operator&() const {
return _cowdata.ptr() + _index;
}

public:
int length() const;
const char32_t *get_data() const;
_FORCE_INLINE_ void operator=(const T &p_other) const {
_cowdata.set(_index, p_other);
}

Char32String(Char32String &&p_str);
void operator=(Char32String &&p_str);
Char32String() {}
~Char32String();
_FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const {
_cowdata.set(_index, p_other.operator T());
}
};

class CharWideString {
template <class T>
class CharStringT {
friend class String;

const wchar_t *_data = nullptr;
int _length = 0;

CharWideString(const wchar_t *str, int length);
CowData<T> _cowdata;
static inline const T _null = 0;

public:
int length() const;
const wchar_t *get_data() const;

CharWideString(CharWideString &&p_str);
void operator=(CharWideString &&p_str);
CharWideString() {}
~CharWideString();
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
_FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); }
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }

_FORCE_INLINE_ T get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ const T &operator[](int p_index) const {
if (unlikely(p_index == _cowdata.size())) {
return _null;
}

return _cowdata.get(p_index);
}
_FORCE_INLINE_ CharProxy<T> operator[](int p_index) { return CharProxy<T>(p_index, _cowdata); }

_FORCE_INLINE_ CharStringT() {}
_FORCE_INLINE_ CharStringT(const CharStringT<T> &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ void operator=(const CharStringT<T> &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ CharStringT(const T *p_cstr) { copy_from(p_cstr); }

void operator=(const T *p_cstr);
bool operator<(const CharStringT<T> &p_right) const;
CharStringT<T> &operator+=(T p_char);
int length() const { return size() ? size() - 1 : 0; }
const T *get_data() const;
operator const T *() const { return get_data(); };

protected:
void copy_from(const T *p_cstr);
};

typedef CharStringT<char> CharString;
typedef CharStringT<char16_t> Char16String;
typedef CharStringT<char32_t> Char32String;
typedef CharStringT<wchar_t> CharWideString;

} // namespace godot

#endif // GODOT_CHAR_STRING_HPP
Loading

0 comments on commit 28494f0

Please sign in to comment.