Merge pull request #68310 from neikeq/csharp-opt-variant-generic-conv

C#: Optimize Variant conversion callbacks
This commit is contained in:
Rémi Verschelde 2022-11-25 19:27:26 +01:00
commit fcdded2e3d
No known key found for this signature in database
GPG Key ID: C3336907360768E1
9 changed files with 556 additions and 1274 deletions

View File

@ -2274,7 +2274,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(");\n");
// Generate Callable trampoline for the delegate
p_output << MEMBER_BEGIN "private static unsafe void " << p_isignal.proxy_name << "Trampoline"
p_output << MEMBER_BEGIN "private static void " << p_isignal.proxy_name << "Trampoline"
<< "(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)\n"
<< INDENT1 "{\n"
<< INDENT2 "Callable.ThrowIfArgCountMismatch(args, " << itos(p_isignal.arguments.size()) << ");\n"
@ -2289,9 +2289,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output << ",";
}
// TODO: We don't need to use VariantConversionCallbacks. We have the type information so we can use [cs_variant_to_managed] and [cs_managed_to_variant].
p_output << "\n" INDENT3 "VariantConversionCallbacks.GetToManagedCallback<"
<< arg_type->cs_type << ">()(args[" << itos(idx) << "])";
p_output << sformat(arg_type->cs_variant_to_managed,
"args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name);
idx++;
}

View File

@ -495,35 +495,10 @@ namespace Godot.Collections
private static Array<T> FromVariantFunc(in godot_variant variant) =>
VariantUtils.ConvertToArrayObject<T>(variant);
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
private static readonly unsafe delegate* managed<in T, godot_variant> ConvertToVariantCallback;
private static readonly unsafe delegate* managed<in godot_variant, T> ConvertToManagedCallback;
// ReSharper restore StaticMemberInGenericType
static unsafe Array()
{
VariantConversionCallbacks.GenericConversionCallbacks[typeof(Array<T>)] =
(
(IntPtr)(delegate* managed<in Array<T>, godot_variant>)&ToVariantFunc,
(IntPtr)(delegate* managed<in godot_variant, Array<T>>)&FromVariantFunc
);
ConvertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
ConvertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
}
private static unsafe void ValidateVariantConversionCallbacks()
{
if (ConvertToVariantCallback == null || ConvertToManagedCallback == null)
{
throw new InvalidOperationException(
$"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'.");
}
VariantUtils.GenericConversion<Array<T>>.ToVariantCb = &ToVariantFunc;
VariantUtils.GenericConversion<Array<T>>.FromVariantCb = &FromVariantFunc;
}
private readonly Array _underlyingArray;
@ -539,8 +514,6 @@ namespace Godot.Collections
/// </summary>
public Array()
{
ValidateVariantConversionCallbacks();
_underlyingArray = new Array();
}
@ -551,8 +524,6 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array(IEnumerable<T> collection)
{
ValidateVariantConversionCallbacks();
if (collection == null)
throw new ArgumentNullException(nameof(collection));
@ -569,8 +540,6 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array(T[] array) : this()
{
ValidateVariantConversionCallbacks();
if (array == null)
throw new ArgumentNullException(nameof(array));
@ -586,8 +555,6 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
ValidateVariantConversionCallbacks();
_underlyingArray = array;
}
@ -665,7 +632,7 @@ namespace Godot.Collections
get
{
_underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
return ConvertToManagedCallback(borrowElem);
return VariantUtils.ConvertTo<T>(borrowElem);
}
set
{
@ -675,7 +642,7 @@ namespace Godot.Collections
godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
godot_variant* itemPtr = &ptrw[index];
(*itemPtr).Dispose();
*itemPtr = ConvertToVariantCallback(value);
*itemPtr = VariantUtils.CreateFrom(value);
}
}
@ -685,9 +652,9 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to search for.</param>
/// <returns>The index of the item, or -1 if not found.</returns>
public unsafe int IndexOf(T item)
public int IndexOf(T item)
{
using var variantValue = ConvertToVariantCallback(item);
using var variantValue = VariantUtils.CreateFrom(item);
var self = (godot_array)_underlyingArray.NativeValue;
return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
}
@ -700,12 +667,12 @@ namespace Godot.Collections
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="item">The item to insert.</param>
public unsafe void Insert(int index, T item)
public void Insert(int index, T item)
{
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException(nameof(index));
using var variantValue = ConvertToVariantCallback(item);
using var variantValue = VariantUtils.CreateFrom(item);
var self = (godot_array)_underlyingArray.NativeValue;
NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
}
@ -736,9 +703,9 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>The new size after adding the item.</returns>
public unsafe void Add(T item)
public void Add(T item)
{
using var variantValue = ConvertToVariantCallback(item);
using var variantValue = VariantUtils.CreateFrom(item);
var self = (godot_array)_underlyingArray.NativeValue;
_ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
}

View File

@ -54,7 +54,7 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 1);
((Action<T0>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
VariantUtils.ConvertTo<T0>(args[0])
);
ret = default;
@ -73,8 +73,8 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 2);
((Action<T0, T1>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1])
);
ret = default;
@ -93,9 +93,9 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 3);
((Action<T0, T1, T2>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2])
);
ret = default;
@ -114,10 +114,10 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 4);
((Action<T0, T1, T2, T3>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3])
);
ret = default;
@ -136,11 +136,11 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 5);
((Action<T0, T1, T2, T3, T4>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4])
);
ret = default;
@ -159,12 +159,12 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 6);
((Action<T0, T1, T2, T3, T4, T5>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5])
);
ret = default;
@ -183,13 +183,13 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 7);
((Action<T0, T1, T2, T3, T4, T5, T6>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5]),
VariantUtils.ConvertTo<T6>(args[6])
);
ret = default;
@ -208,14 +208,14 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 8);
((Action<T0, T1, T2, T3, T4, T5, T6, T7>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5]),
VariantUtils.ConvertTo<T6>(args[6]),
VariantUtils.ConvertTo<T7>(args[7])
);
ret = default;
@ -234,15 +234,15 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 9);
((Action<T0, T1, T2, T3, T4, T5, T6, T7, T8>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5]),
VariantUtils.ConvertTo<T6>(args[6]),
VariantUtils.ConvertTo<T7>(args[7]),
VariantUtils.ConvertTo<T8>(args[8])
);
ret = default;
@ -265,7 +265,7 @@ public readonly partial struct Callable
TResult res = ((Func<TResult>)delegateObj)();
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -281,10 +281,10 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 1);
TResult res = ((Func<T0, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
VariantUtils.ConvertTo<T0>(args[0])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -300,11 +300,11 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 2);
TResult res = ((Func<T0, T1, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -320,12 +320,12 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 3);
TResult res = ((Func<T0, T1, T2, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -341,13 +341,13 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 4);
TResult res = ((Func<T0, T1, T2, T3, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -363,14 +363,14 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 5);
TResult res = ((Func<T0, T1, T2, T3, T4, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -386,15 +386,15 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 6);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -410,16 +410,16 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 7);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5]),
VariantUtils.ConvertTo<T6>(args[6])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -435,17 +435,17 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 8);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5]),
VariantUtils.ConvertTo<T6>(args[6]),
VariantUtils.ConvertTo<T7>(args[7])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);
@ -461,18 +461,18 @@ public readonly partial struct Callable
ThrowIfArgCountMismatch(args, 9);
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
VariantUtils.ConvertTo<T0>(args[0]),
VariantUtils.ConvertTo<T1>(args[1]),
VariantUtils.ConvertTo<T2>(args[2]),
VariantUtils.ConvertTo<T3>(args[3]),
VariantUtils.ConvertTo<T4>(args[4]),
VariantUtils.ConvertTo<T5>(args[5]),
VariantUtils.ConvertTo<T6>(args[6]),
VariantUtils.ConvertTo<T7>(args[7]),
VariantUtils.ConvertTo<T8>(args[8])
);
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
ret = VariantUtils.CreateFrom(res);
}
return CreateWithUnsafeTrampoline(func, &Trampoline);

View File

@ -362,45 +362,10 @@ namespace Godot.Collections
private static Dictionary<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
VariantUtils.ConvertToDictionaryObject<TKey, TValue>(variant);
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
private static readonly unsafe delegate* managed<in TKey, godot_variant> ConvertKeyToVariantCallback;
private static readonly unsafe delegate* managed<in godot_variant, TKey> ConvertKeyToManagedCallback;
private static readonly unsafe delegate* managed<in TValue, godot_variant> ConvertValueToVariantCallback;
private static readonly unsafe delegate* managed<in godot_variant, TValue> ConvertValueToManagedCallback;
// ReSharper restore StaticMemberInGenericType
static unsafe Dictionary()
{
VariantConversionCallbacks.GenericConversionCallbacks[typeof(Dictionary<TKey, TValue>)] =
(
(IntPtr)(delegate* managed<in Dictionary<TKey, TValue>, godot_variant>)&ToVariantFunc,
(IntPtr)(delegate* managed<in godot_variant, Dictionary<TKey, TValue>>)&FromVariantFunc
);
ConvertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
ConvertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
ConvertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
ConvertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
}
private static unsafe void ValidateVariantConversionCallbacks()
{
if (ConvertKeyToVariantCallback == null || ConvertKeyToManagedCallback == null)
{
throw new InvalidOperationException(
$"The dictionary key type is not supported for conversion to Variant: '{typeof(TKey).FullName}'.");
}
if (ConvertValueToVariantCallback == null || ConvertValueToManagedCallback == null)
{
throw new InvalidOperationException(
$"The dictionary value type is not supported for conversion to Variant: '{typeof(TValue).FullName}'.");
}
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.ToVariantCb = &ToVariantFunc;
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.FromVariantCb = &FromVariantFunc;
}
private readonly Dictionary _underlyingDict;
@ -416,8 +381,6 @@ namespace Godot.Collections
/// </summary>
public Dictionary()
{
ValidateVariantConversionCallbacks();
_underlyingDict = new Dictionary();
}
@ -428,8 +391,6 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
ValidateVariantConversionCallbacks();
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
@ -446,8 +407,6 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
ValidateVariantConversionCallbacks();
_underlyingDict = dictionary;
}
@ -481,18 +440,18 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
/// <value>The value at the given <paramref name="key"/>.</value>
public unsafe TValue this[TKey key]
public TValue this[TKey key]
{
get
{
using var variantKey = ConvertKeyToVariantCallback(key);
using var variantKey = VariantUtils.CreateFrom(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant value).ToBool())
{
using (value)
return ConvertValueToManagedCallback(value);
return VariantUtils.ConvertTo<TValue>(value);
}
else
{
@ -501,8 +460,8 @@ namespace Godot.Collections
}
set
{
using var variantKey = ConvertKeyToVariantCallback(key);
using var variantValue = ConvertValueToVariantCallback(value);
using var variantKey = VariantUtils.CreateFrom(key);
using var variantValue = VariantUtils.CreateFrom(value);
var self = (godot_dictionary)_underlyingDict.NativeValue;
NativeFuncs.godotsharp_dictionary_set_value(ref self,
variantKey, variantValue);
@ -541,7 +500,7 @@ namespace Godot.Collections
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
private unsafe KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
var self = (godot_dictionary)_underlyingDict.NativeValue;
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
@ -551,8 +510,8 @@ namespace Godot.Collections
using (value)
{
return new KeyValuePair<TKey, TValue>(
ConvertKeyToManagedCallback(key),
ConvertValueToManagedCallback(value));
VariantUtils.ConvertTo<TKey>(key),
VariantUtils.ConvertTo<TValue>(value));
}
}
@ -562,15 +521,15 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
public unsafe void Add(TKey key, TValue value)
public void Add(TKey key, TValue value)
{
using var variantKey = ConvertKeyToVariantCallback(key);
using var variantKey = VariantUtils.CreateFrom(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
throw new ArgumentException("An element with the same key already exists.", nameof(key));
using var variantValue = ConvertValueToVariantCallback(value);
using var variantValue = VariantUtils.CreateFrom(value);
NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
}
@ -579,9 +538,9 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
public unsafe bool ContainsKey(TKey key)
public bool ContainsKey(TKey key)
{
using var variantKey = ConvertKeyToVariantCallback(key);
using var variantKey = VariantUtils.CreateFrom(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
}
@ -590,9 +549,9 @@ namespace Godot.Collections
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
public unsafe bool Remove(TKey key)
public bool Remove(TKey key)
{
using var variantKey = ConvertKeyToVariantCallback(key);
using var variantKey = VariantUtils.CreateFrom(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
}
@ -603,15 +562,15 @@ namespace Godot.Collections
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
using var variantKey = ConvertKeyToVariantCallback(key);
using var variantKey = VariantUtils.CreateFrom(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
using (retValue)
value = found ? ConvertValueToManagedCallback(retValue) : default;
value = found ? VariantUtils.ConvertTo<TValue>(retValue) : default;
return found;
}
@ -635,9 +594,9 @@ namespace Godot.Collections
/// </summary>
public void Clear() => _underlyingDict.Clear();
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
using var variantKey = ConvertKeyToVariantCallback(item.Key);
using var variantKey = VariantUtils.CreateFrom(item.Key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
@ -647,7 +606,7 @@ namespace Godot.Collections
if (!found)
return false;
using var variantValue = ConvertValueToVariantCallback(item.Value);
using var variantValue = VariantUtils.CreateFrom(item.Value);
return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
}
}
@ -680,9 +639,9 @@ namespace Godot.Collections
}
}
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
using var variantKey = ConvertKeyToVariantCallback(item.Key);
using var variantKey = VariantUtils.CreateFrom(item.Key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
@ -692,7 +651,7 @@ namespace Godot.Collections
if (!found)
return false;
using var variantValue = ConvertValueToVariantCallback(item.Value);
using var variantValue = VariantUtils.CreateFrom(item.Value);
if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
{
return NativeFuncs.godotsharp_dictionary_remove_key(

View File

@ -8,7 +8,7 @@ using Godot.Collections;
namespace Godot.NativeInterop
{
public static class VariantUtils
public static partial class VariantUtils
{
public static godot_variant CreateFromRID(RID from)
=> new() { Type = Variant.Type.Rid, RID = from };

View File

@ -0,0 +1,406 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Godot.NativeInterop;
public partial class VariantUtils
{
private static Exception UnsupportedType<T>() => throw new InvalidOperationException(
$"The type is not supported for conversion to/from Variant: '{typeof(T).FullName}'");
internal static class GenericConversion<T>
{
public static unsafe godot_variant ToVariant(in T from) =>
ToVariantCb != null ? ToVariantCb(from) : throw UnsupportedType<T>();
public static unsafe T FromVariant(in godot_variant variant) =>
FromVariantCb != null ? FromVariantCb(variant) : throw UnsupportedType<T>();
// ReSharper disable once StaticMemberInGenericType
internal static unsafe delegate*<in T, godot_variant> ToVariantCb;
// ReSharper disable once StaticMemberInGenericType
internal static unsafe delegate*<in godot_variant, T> FromVariantCb;
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
static GenericConversion()
{
RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
public static godot_variant CreateFrom<[MustBeVariant] T>(in T from)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static TTo UnsafeAs<TTo>(in T f) => Unsafe.As<T, TTo>(ref Unsafe.AsRef(f));
// `typeof(T) == typeof(X)` is optimized away. We cannot cache `typeof(T)` in a local variable, as it's not optimized when done like that.
if (typeof(T) == typeof(bool))
return CreateFromBool(UnsafeAs<bool>(from));
if (typeof(T) == typeof(char))
return CreateFromInt(UnsafeAs<char>(from));
if (typeof(T) == typeof(sbyte))
return CreateFromInt(UnsafeAs<sbyte>(from));
if (typeof(T) == typeof(short))
return CreateFromInt(UnsafeAs<short>(from));
if (typeof(T) == typeof(int))
return CreateFromInt(UnsafeAs<int>(from));
if (typeof(T) == typeof(long))
return CreateFromInt(UnsafeAs<long>(from));
if (typeof(T) == typeof(byte))
return CreateFromInt(UnsafeAs<byte>(from));
if (typeof(T) == typeof(ushort))
return CreateFromInt(UnsafeAs<ushort>(from));
if (typeof(T) == typeof(uint))
return CreateFromInt(UnsafeAs<uint>(from));
if (typeof(T) == typeof(ulong))
return CreateFromInt(UnsafeAs<ulong>(from));
if (typeof(T) == typeof(float))
return CreateFromFloat(UnsafeAs<float>(from));
if (typeof(T) == typeof(double))
return CreateFromFloat(UnsafeAs<double>(from));
if (typeof(T) == typeof(Vector2))
return CreateFromVector2(UnsafeAs<Vector2>(from));
if (typeof(T) == typeof(Vector2i))
return CreateFromVector2i(UnsafeAs<Vector2i>(from));
if (typeof(T) == typeof(Rect2))
return CreateFromRect2(UnsafeAs<Rect2>(from));
if (typeof(T) == typeof(Rect2i))
return CreateFromRect2i(UnsafeAs<Rect2i>(from));
if (typeof(T) == typeof(Transform2D))
return CreateFromTransform2D(UnsafeAs<Transform2D>(from));
if (typeof(T) == typeof(Vector3))
return CreateFromVector3(UnsafeAs<Vector3>(from));
if (typeof(T) == typeof(Vector3i))
return CreateFromVector3i(UnsafeAs<Vector3i>(from));
if (typeof(T) == typeof(Basis))
return CreateFromBasis(UnsafeAs<Basis>(from));
if (typeof(T) == typeof(Quaternion))
return CreateFromQuaternion(UnsafeAs<Quaternion>(from));
if (typeof(T) == typeof(Transform3D))
return CreateFromTransform3D(UnsafeAs<Transform3D>(from));
if (typeof(T) == typeof(Vector4))
return CreateFromVector4(UnsafeAs<Vector4>(from));
if (typeof(T) == typeof(Vector4i))
return CreateFromVector4i(UnsafeAs<Vector4i>(from));
if (typeof(T) == typeof(AABB))
return CreateFromAABB(UnsafeAs<AABB>(from));
if (typeof(T) == typeof(Color))
return CreateFromColor(UnsafeAs<Color>(from));
if (typeof(T) == typeof(Plane))
return CreateFromPlane(UnsafeAs<Plane>(from));
if (typeof(T) == typeof(Callable))
return CreateFromCallable(UnsafeAs<Callable>(from));
if (typeof(T) == typeof(SignalInfo))
return CreateFromSignalInfo(UnsafeAs<SignalInfo>(from));
if (typeof(T) == typeof(string))
return CreateFromString(UnsafeAs<string>(from));
if (typeof(T) == typeof(byte[]))
return CreateFromPackedByteArray(UnsafeAs<byte[]>(from));
if (typeof(T) == typeof(int[]))
return CreateFromPackedInt32Array(UnsafeAs<int[]>(from));
if (typeof(T) == typeof(long[]))
return CreateFromPackedInt64Array(UnsafeAs<long[]>(from));
if (typeof(T) == typeof(float[]))
return CreateFromPackedFloat32Array(UnsafeAs<float[]>(from));
if (typeof(T) == typeof(double[]))
return CreateFromPackedFloat64Array(UnsafeAs<double[]>(from));
if (typeof(T) == typeof(string[]))
return CreateFromPackedStringArray(UnsafeAs<string[]>(from));
if (typeof(T) == typeof(Vector2[]))
return CreateFromPackedVector2Array(UnsafeAs<Vector2[]>(from));
if (typeof(T) == typeof(Vector3[]))
return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
if (typeof(T) == typeof(Color[]))
return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
if (typeof(T) == typeof(StringName[]))
return CreateFromSystemArrayOfStringName(UnsafeAs<StringName[]>(from));
if (typeof(T) == typeof(NodePath[]))
return CreateFromSystemArrayOfNodePath(UnsafeAs<NodePath[]>(from));
if (typeof(T) == typeof(RID[]))
return CreateFromSystemArrayOfRID(UnsafeAs<RID[]>(from));
if (typeof(T) == typeof(StringName))
return CreateFromStringName(UnsafeAs<StringName>(from));
if (typeof(T) == typeof(NodePath))
return CreateFromNodePath(UnsafeAs<NodePath>(from));
if (typeof(T) == typeof(RID))
return CreateFromRID(UnsafeAs<RID>(from));
if (typeof(T) == typeof(Godot.Collections.Dictionary))
return CreateFromDictionary(UnsafeAs<Godot.Collections.Dictionary>(from));
if (typeof(T) == typeof(Godot.Collections.Array))
return CreateFromArray(UnsafeAs<Godot.Collections.Array>(from));
if (typeof(T) == typeof(Variant))
return NativeFuncs.godotsharp_variant_new_copy((godot_variant)UnsafeAs<Variant>(from).NativeVar);
// More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
// `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
if (typeof(Godot.Object).IsAssignableFrom(typeof(T)))
return CreateFromGodotObject(UnsafeAs<Godot.Object>(from));
// `typeof(T).IsValueType` is optimized away
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
{
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
// We don't need to know whether it's signed or unsigned.
if (Unsafe.SizeOf<T>() == 1)
return CreateFromInt(UnsafeAs<sbyte>(from));
if (Unsafe.SizeOf<T>() == 2)
return CreateFromInt(UnsafeAs<short>(from));
if (Unsafe.SizeOf<T>() == 4)
return CreateFromInt(UnsafeAs<int>(from));
if (Unsafe.SizeOf<T>() == 8)
return CreateFromInt(UnsafeAs<long>(from));
throw UnsupportedType<T>();
}
return GenericConversion<T>.ToVariant(from);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
public static T ConvertTo<[MustBeVariant] T>(in godot_variant variant)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static T UnsafeAsT<TFrom>(TFrom f) => Unsafe.As<TFrom, T>(ref Unsafe.AsRef(f));
if (typeof(T) == typeof(bool))
return UnsafeAsT(ConvertToBool(variant));
if (typeof(T) == typeof(char))
return UnsafeAsT(ConvertToChar(variant));
if (typeof(T) == typeof(sbyte))
return UnsafeAsT(ConvertToInt8(variant));
if (typeof(T) == typeof(short))
return UnsafeAsT(ConvertToInt16(variant));
if (typeof(T) == typeof(int))
return UnsafeAsT(ConvertToInt32(variant));
if (typeof(T) == typeof(long))
return UnsafeAsT(ConvertToInt64(variant));
if (typeof(T) == typeof(byte))
return UnsafeAsT(ConvertToUInt8(variant));
if (typeof(T) == typeof(ushort))
return UnsafeAsT(ConvertToUInt16(variant));
if (typeof(T) == typeof(uint))
return UnsafeAsT(ConvertToUInt32(variant));
if (typeof(T) == typeof(ulong))
return UnsafeAsT(ConvertToUInt64(variant));
if (typeof(T) == typeof(float))
return UnsafeAsT(ConvertToFloat32(variant));
if (typeof(T) == typeof(double))
return UnsafeAsT(ConvertToFloat64(variant));
if (typeof(T) == typeof(Vector2))
return UnsafeAsT(ConvertToVector2(variant));
if (typeof(T) == typeof(Vector2i))
return UnsafeAsT(ConvertToVector2i(variant));
if (typeof(T) == typeof(Rect2))
return UnsafeAsT(ConvertToRect2(variant));
if (typeof(T) == typeof(Rect2i))
return UnsafeAsT(ConvertToRect2i(variant));
if (typeof(T) == typeof(Transform2D))
return UnsafeAsT(ConvertToTransform2D(variant));
if (typeof(T) == typeof(Vector3))
return UnsafeAsT(ConvertToVector3(variant));
if (typeof(T) == typeof(Vector3i))
return UnsafeAsT(ConvertToVector3i(variant));
if (typeof(T) == typeof(Basis))
return UnsafeAsT(ConvertToBasis(variant));
if (typeof(T) == typeof(Quaternion))
return UnsafeAsT(ConvertToQuaternion(variant));
if (typeof(T) == typeof(Transform3D))
return UnsafeAsT(ConvertToTransform3D(variant));
if (typeof(T) == typeof(Vector4))
return UnsafeAsT(ConvertToVector4(variant));
if (typeof(T) == typeof(Vector4i))
return UnsafeAsT(ConvertToVector4i(variant));
if (typeof(T) == typeof(AABB))
return UnsafeAsT(ConvertToAABB(variant));
if (typeof(T) == typeof(Color))
return UnsafeAsT(ConvertToColor(variant));
if (typeof(T) == typeof(Plane))
return UnsafeAsT(ConvertToPlane(variant));
if (typeof(T) == typeof(Callable))
return UnsafeAsT(ConvertToCallableManaged(variant));
if (typeof(T) == typeof(SignalInfo))
return UnsafeAsT(ConvertToSignalInfo(variant));
if (typeof(T) == typeof(string))
return UnsafeAsT(ConvertToStringObject(variant));
if (typeof(T) == typeof(byte[]))
return UnsafeAsT(ConvertAsPackedByteArrayToSystemArray(variant));
if (typeof(T) == typeof(int[]))
return UnsafeAsT(ConvertAsPackedInt32ArrayToSystemArray(variant));
if (typeof(T) == typeof(long[]))
return UnsafeAsT(ConvertAsPackedInt64ArrayToSystemArray(variant));
if (typeof(T) == typeof(float[]))
return UnsafeAsT(ConvertAsPackedFloat32ArrayToSystemArray(variant));
if (typeof(T) == typeof(double[]))
return UnsafeAsT(ConvertAsPackedFloat64ArrayToSystemArray(variant));
if (typeof(T) == typeof(string[]))
return UnsafeAsT(ConvertAsPackedStringArrayToSystemArray(variant));
if (typeof(T) == typeof(Vector2[]))
return UnsafeAsT(ConvertAsPackedVector2ArrayToSystemArray(variant));
if (typeof(T) == typeof(Vector3[]))
return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant));
if (typeof(T) == typeof(Color[]))
return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant));
if (typeof(T) == typeof(StringName[]))
return UnsafeAsT(ConvertToSystemArrayOfStringName(variant));
if (typeof(T) == typeof(NodePath[]))
return UnsafeAsT(ConvertToSystemArrayOfNodePath(variant));
if (typeof(T) == typeof(RID[]))
return UnsafeAsT(ConvertToSystemArrayOfRID(variant));
if (typeof(T) == typeof(StringName))
return UnsafeAsT(ConvertToStringNameObject(variant));
if (typeof(T) == typeof(NodePath))
return UnsafeAsT(ConvertToNodePathObject(variant));
if (typeof(T) == typeof(RID))
return UnsafeAsT(ConvertToRID(variant));
if (typeof(T) == typeof(Godot.Collections.Dictionary))
return UnsafeAsT(ConvertToDictionaryObject(variant));
if (typeof(T) == typeof(Godot.Collections.Array))
return UnsafeAsT(ConvertToArrayObject(variant));
if (typeof(T) == typeof(Variant))
return UnsafeAsT(Variant.CreateCopyingBorrowed(variant));
// More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
// `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
if (typeof(Godot.Object).IsAssignableFrom(typeof(T)))
return (T)(object)ConvertToGodotObject(variant);
// `typeof(T).IsValueType` is optimized away
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
{
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
// We don't need to know whether it's signed or unsigned.
if (Unsafe.SizeOf<T>() == 1)
return UnsafeAsT(ConvertToInt8(variant));
if (Unsafe.SizeOf<T>() == 2)
return UnsafeAsT(ConvertToInt16(variant));
if (Unsafe.SizeOf<T>() == 4)
return UnsafeAsT(ConvertToInt32(variant));
if (Unsafe.SizeOf<T>() == 8)
return UnsafeAsT(ConvertToInt64(variant));
throw UnsupportedType<T>();
}
return GenericConversion<T>.FromVariant(variant);
}
}

View File

@ -120,6 +120,14 @@ public partial struct Variant : IDisposable
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<[MustBeVariant] T>(in T from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFrom(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T As<[MustBeVariant] T>() =>
VariantUtils.ConvertTo<T>(NativeVar.DangerousSelfRef);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AsBool() =>
VariantUtils.ConvertToBool((godot_variant)NativeVar);

View File

@ -101,9 +101,9 @@
<Compile Include="Core\NativeInterop\InteropUtils.cs" />
<Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
<Compile Include="Core\NativeInterop\NativeVariantPtrArgs.cs" />
<Compile Include="Core\NativeInterop\VariantConversionCallbacks.cs" />
<Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" />
<Compile Include="Core\NativeInterop\VariantUtils.cs" />
<Compile Include="Core\NativeInterop\VariantUtils.generic.cs" />
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
<Compile Include="Core\Object.exceptions.cs" />
@ -123,6 +123,7 @@
<Compile Include="Core\StringName.cs" />
<Compile Include="Core\Transform2D.cs" />
<Compile Include="Core\Transform3D.cs" />
<Compile Include="Core\Variant.cs" />
<Compile Include="Core\Vector2.cs" />
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
@ -131,7 +132,6 @@
<Compile Include="Core\Vector4i.cs" />
<Compile Include="GlobalUsings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Variant.cs" />
</ItemGroup>
<!--
We import a props file with auto-generated includes. This works well with Rider.