Skip to content

Commit

Permalink
CSharp struct with unique internals
Browse files Browse the repository at this point in the history
  • Loading branch information
RobProductions committed Sep 15, 2024
1 parent 5e6a92b commit cfe67d2
Show file tree
Hide file tree
Showing 15 changed files with 2,079 additions and 2 deletions.
12 changes: 12 additions & 0 deletions core/variant/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2331,6 +2331,18 @@ Variant::operator PackedVector4Array() const {
}
}

Variant::operator StructInfo() const {
if (is_struct()) {
//StructInfo info = operator StructInfo();
//return Array(&info);
//TODO: Confusion
}

//return _convert_array_from_variant<StructInfo>(*this);
Array va = operator Array();
return *va.get_struct_info();
}

/* helpers */

Variant::operator Vector<::RID>() const {
Expand Down
2 changes: 2 additions & 0 deletions core/variant/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ class Variant {
operator PackedColorArray() const;
operator PackedVector4Array() const;

operator StructInfo() const;

operator Vector<::RID>() const;
operator Vector<Plane>() const;
operator Vector<Face3>() const;
Expand Down
9 changes: 8 additions & 1 deletion modules/mono/editor/bindings_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2656,6 +2656,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
"Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'.");

if (p_imethod.proxy_name == "ClassGetSignal") {
_log("YO: %s\n", p_imethod.name);
}

if (p_itype.api_type == ClassDB::API_CORE) {
ERR_FAIL_COND_V_MSG(return_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
"Method '" + p_itype.name + "." + p_imethod.name + "' has return type '" + return_type->name +
Expand Down Expand Up @@ -3389,7 +3393,7 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface
String params = "<";
for (const TypeReference &param_type : p_generic_type_parameters) {
const TypeInterface *param_itype = _get_type_or_singleton_or_null(param_type);
ERR_FAIL_NULL_V(param_itype, ""); // Parameter type not found
ERR_FAIL_NULL_V_MSG(param_itype, "", "Parameter type '" + param_type.cname + "' was not found.");

ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
"Generic type parameter is a singleton: '" + param_itype->name + "'.");
Expand Down Expand Up @@ -4371,6 +4375,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_STRUCT_TYPE(Vector4, Vector4)
INSERT_STRUCT_TYPE(Vector4i, Vector4I)
INSERT_STRUCT_TYPE(Projection, Projection)
INSERT_STRUCT_TYPE(StructInfo, StructInfo)

#undef INSERT_STRUCT_TYPE

Expand Down Expand Up @@ -4607,6 +4612,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3);
INSERT_ARRAY(PackedVector4Array, godot_packed_vector4_array, Vector4);

INSERT_ARRAY(Struct, godot_struct, Struct)

#undef INSERT_ARRAY

// Array
Expand Down
3 changes: 2 additions & 1 deletion modules/mono/editor/bindings_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ class BindingsGenerator {
StringName type_Vector4i = StaticCString::create("Vector4i");

// Object not included as it must be checked for all derived classes
static constexpr int nullable_types_count = 19;
static constexpr int nullable_types_count = 20;
StringName nullable_types[nullable_types_count] = {
type_String,
type_StringName,
Expand All @@ -714,6 +714,7 @@ class BindingsGenerator {
type_Array_generic,
type_Dictionary_generic,
StaticCString::create(_STR(Array)),
StaticCString::create(_STR(Struct)),
StaticCString::create(_STR(Dictionary)),
StaticCString::create(_STR(Callable)),
StaticCString::create(_STR(Signal)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,22 @@ public static unsafe ref godot_array AsRef(godot_array* source)
public static unsafe ref godot_array AsRef(in godot_array source)
=> ref *ReadOnlyRefAsPointer(in source);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_struct* AsPointer(ref godot_struct value)
=> value.GetUnsafeAddress();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_struct* ReadOnlyRefAsPointer(in godot_struct value)
=> value.GetUnsafeAddress();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref godot_struct AsRef(godot_struct* source)
=> ref *source;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref godot_struct AsRef(in godot_struct source)
=> ref *ReadOnlyRefAsPointer(in source);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_dictionary* AsPointer(ref godot_dictionary value)
=> value.GetUnsafeAddress();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,106 @@ public static unsafe explicit operator godot_array(movable value)
}
}

// A correctly constructed value needs to call the native default constructor to allocate `_p`.
// Don't pass a C# default constructed `godot_struct` to native code, unless it's going to
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Explicit)]
public ref struct godot_struct
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal readonly unsafe godot_struct* GetUnsafeAddress()
=> (godot_struct*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));

[FieldOffset(0)] private byte _getUnsafeAddressHelper;

// Internally, a Struct is just an Array which utilizes ArrayPrivate
[FieldOffset(0)] private unsafe ArrayPrivate* _p;

[StructLayout(LayoutKind.Sequential)]
private struct ArrayPrivate
{
private uint _safeRefCount;

public VariantVector _arrayVector;

private unsafe godot_variant* _readOnly;

// There are more fields here, but we don't care as we never store this in C#

public readonly int Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _arrayVector.Size;
}

public readonly unsafe bool IsReadOnly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _readOnly != null;
}
}

[StructLayout(LayoutKind.Sequential)]
private struct VariantVector
{
private IntPtr _writeProxy;
public unsafe godot_variant* _ptr;

public readonly unsafe int Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _ptr != null ? (int)(*((ulong*)_ptr - 1)) : 0;
}
}

public readonly unsafe godot_variant* Elements
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _p->_arrayVector._ptr;
}

public readonly unsafe bool IsAllocated
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _p != null;
}

public readonly unsafe int Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _p != null ? _p->Size : 0;
}

public readonly unsafe bool IsReadOnly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _p != null && _p->IsReadOnly;
}

public unsafe void Dispose()
{
if (_p == null)
return;
NativeFuncs.godotsharp_struct_destroy(ref this);
_p = null;
}

[StructLayout(LayoutKind.Sequential)]
internal struct movable
{
private unsafe ArrayPrivate* _p;

public static unsafe explicit operator movable(in godot_struct value)
=> *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));

public static unsafe explicit operator godot_struct(movable value)
=> *(godot_struct*)Unsafe.AsPointer(ref value);

public unsafe ref godot_struct DangerousSelfRef =>
ref CustomUnsafe.AsRef((godot_struct*)Unsafe.AsPointer(ref this));
}
}

// IMPORTANT:
// A correctly constructed value needs to call the native default constructor to allocate `_p`.
// Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ internal static Variant.Type ConvertManagedTypeToVariantType(Type type, out bool

if (typeof(Collections.Array) == type)
return Variant.Type.Array;

if (typeof(Collections.Struct) == type)
return Variant.Type.Array;
}

break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ public static partial void godotsharp_variant_new_dictionary(out godot_variant r

public static partial void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr);

public static partial void godotsharp_variant_new_struct(out godot_variant r_dest, in godot_struct p_arr);

public static partial void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest,
in godot_packed_byte_array p_pba);

Expand Down Expand Up @@ -287,6 +289,8 @@ public static partial void godotsharp_variant_new_packed_color_array(out godot_v

public static partial godot_array godotsharp_variant_as_array(in godot_variant p_self);

public static partial godot_struct godotsharp_variant_as_struct(in godot_variant p_self);

public static partial godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self);

public static partial godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self);
Expand Down Expand Up @@ -336,6 +340,14 @@ public static partial void godotsharp_string_name_new_copy(out godot_string_name

public static partial godot_variant* godotsharp_array_ptrw(ref godot_array p_self);

// struct.h

public static partial void godotsharp_struct_new(out godot_struct r_dest);

public static partial void godotsharp_struct_new_copy(out godot_struct r_dest, in godot_struct p_src);

public static partial godot_variant* godotsharp_struct_ptrw(ref godot_struct p_self);

// dictionary.h

public static partial void godotsharp_dictionary_new(out godot_dictionary r_dest);
Expand Down Expand Up @@ -379,6 +391,8 @@ public static partial void godotsharp_dictionary_new_copy(out godot_dictionary r

public static partial void godotsharp_array_destroy(ref godot_array p_self);

public static partial void godotsharp_struct_destroy(ref godot_struct p_self);

public static partial void godotsharp_dictionary_destroy(ref godot_dictionary p_self);

// Array
Expand Down Expand Up @@ -425,6 +439,42 @@ public static partial void godotsharp_array_slice(ref godot_array p_self, int p_

public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str);

// Struct

public static partial int godotsharp_struct_add(ref godot_struct p_self, in godot_variant p_item);

public static partial int godotsharp_struct_add_range(ref godot_struct p_self, in godot_struct p_collection);

public static partial int godotsharp_struct_binary_search(ref godot_struct p_self, int p_index, int p_count, in godot_variant p_value);

public static partial void
godotsharp_struct_duplicate(ref godot_struct p_self, godot_bool p_deep, out godot_struct r_dest);

public static partial void godotsharp_struct_fill(ref godot_struct p_self, in godot_variant p_value);

public static partial int godotsharp_struct_index_of(ref godot_struct p_self, in godot_variant p_item, int p_index = 0);

public static partial void godotsharp_struct_insert(ref godot_struct p_self, int p_index, in godot_variant p_item);

public static partial int godotsharp_struct_last_index_of(ref godot_struct p_self, in godot_variant p_item, int p_index);

public static partial void godotsharp_struct_make_read_only(ref godot_struct p_self);

public static partial void godotsharp_struct_max(ref godot_struct p_self, out godot_variant r_value);

public static partial void godotsharp_struct_min(ref godot_struct p_self, out godot_variant r_value);

public static partial godot_bool godotsharp_struct_recursive_equal(ref godot_struct p_self, in godot_struct p_other);

public static partial void godotsharp_struct_remove_at(ref godot_struct p_self, int p_index);

public static partial Error godotsharp_struct_resize(ref godot_struct p_self, int p_new_size);

public static partial void godotsharp_struct_slice(ref godot_struct p_self, int p_start, int p_end,
int p_step, godot_bool p_deep, out godot_struct r_dest);

public static partial void godotsharp_struct_to_string(ref godot_struct p_self, out godot_string r_str);

// Dictionary

public static partial godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ public static godot_array godotsharp_array_new_copy(in godot_array src)
return ret;
}

public static godot_struct godotsharp_struct_new()
{
godotsharp_struct_new(out godot_struct ret);
return ret;
}

public static godot_struct godotsharp_struct_new_copy(in godot_struct src)
{
godotsharp_struct_new_copy(out godot_struct ret, src);
return ret;
}

public static godot_dictionary godotsharp_dictionary_new()
{
godotsharp_dictionary_new(out godot_dictionary ret);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@ public static godot_variant CreateFromArray(Collections.Array? from)
public static godot_variant CreateFromArray<[MustBeVariant] T>(Array<T>? from)
=> from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;

public static godot_variant CreateFromStruct(godot_struct from)
{
NativeFuncs.godotsharp_variant_new_struct(out godot_variant ret, from);
return ret;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromStruct(Collections.Struct? from)
=> from != null ? CreateFromStruct((godot_struct)from.NativeValue) : default;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromStruct<[MustBeVariant] T>(Struct<T>? from)
=> from != null ? CreateFromStruct((godot_struct)((Collections.Struct)from).NativeValue) : default;

public static godot_variant CreateFromDictionary(godot_dictionary from)
{
NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from);
Expand Down Expand Up @@ -557,6 +571,15 @@ public static Collections.Array ConvertToArray(in godot_variant p_var)
public static Array<T> ConvertToArray<[MustBeVariant] T>(in godot_variant p_var)
=> Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));

// TODO: should perhaps convert array to struct if type == Type.Array like
// godot_array ConvertToNativeArray above
public static godot_struct ConvertToNativeStruct(in godot_variant p_var)
=> NativeFuncs.godotsharp_variant_as_struct(p_var);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Struct<T> ConvertToStruct<[MustBeVariant] T>(in godot_variant p_var)
=> Struct<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeStruct(p_var));

public static godot_dictionary ConvertToNativeDictionary(in godot_variant p_var)
=> p_var.Type == Variant.Type.Dictionary ?
NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) :
Expand Down
Loading

0 comments on commit cfe67d2

Please sign in to comment.