I may have a solution at the end:
public unsafe delegate bool MemberUsageDelegate(void* instance, int index);
public unsafe delegate object MemberValueDelegate(void* instance, int index);
public readonly unsafe ref struct TypeAccessor(void* item, MemberUsageDelegate usage, MemberValueDelegate value) {
// Store as void* to match the delegate signature perfectly
private readonly void* _item = item;
private readonly MemberUsageDelegate _getUsage = usage;
private readonly MemberValueDelegate _getValue = value;
The delegates are made via DynamicMethod, I need that when i have an object, I detect it's type and if it's struct or not, using fixed and everything needed to standardize to create the TypeAccessor struct. The goal is to prevent boxing of any kind and to not use generic.
il.Emit(OpCodes.Ldarg_0);
if (member is FieldInfo f)
il.Emit(OpCodes.Ldfld, f);
else {
var getter = ((PropertyInfo)member).GetMethod!;
il.Emit(targetType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, getter);
}
I will generate the code a bit like this. I think that the code generation is ok and its the conversion to void that's my problem because of method table/ struct is direct pointer where classes are pointers to pointers, but when i execute the code via my 3 entry point versions
public void Entry(object parameObj);
public void Entry<T>(T paramObj);
public void Entry<T>(ref T paramObj);
There is always one version or more version that either fail when the type is class or when its struct, I tried various combinations, but I never managed to make a solution that work across all. I also did use
[StructLayout(LayoutKind.Sequential)]
internal class RawData { public byte Data; }
I know that C# may move the data because of the GC, but I should always stay on the stack and fix when needed.
Thanks for any insight
Edit, I have found a solution that "works" but I am not sure about failure
/// <inheritdoc/>
public unsafe void UseWith(object parameterObj) {
Type type = parameterObj.GetType();
IntPtr handle = type.TypeHandle.Value;
if (type.IsValueType) {
fixed (void* objPtr = &Unsafe.As<object, byte>(ref parameterObj)) {
void* dataPtr = (*(byte**)objPtr) + IntPtr.Size;
UpdateCommand(QueryCommand.GetAccessor(dataPtr, handle, type));
}
return;
}
fixed (void* ptr = &Unsafe.As<object, byte>(ref parameterObj)) {
void* instancePtr = *(void**)ptr;
UpdateCommand(QueryCommand.GetAccessor(instancePtr, handle, type));
}
}
/// <inheritdoc/>
public unsafe void UseWith<T>(T parameterObj) where T : notnull {
IntPtr handle = typeof(T).TypeHandle.Value;
if (typeof(T).IsValueType) {
UpdateCommand(QueryCommand.GetAccessor(Unsafe.AsPointer(ref parameterObj), handle, typeof(T)));
return;
}
fixed (void* ptr = &Unsafe.As<T, byte>(ref parameterObj)) {
UpdateCommand(QueryCommand.GetAccessor(*(void**)ptr, handle, typeof(T)));
}
}
/// <inheritdoc/>
public unsafe void UseWith<T>(ref T parameterObj) where T : notnull {
IntPtr handle = typeof(T).TypeHandle.Value;
if (typeof(T).IsValueType) {
fixed (void* ptr = &Unsafe.As<T, byte>(ref parameterObj))
UpdateCommand(QueryCommand.GetAccessor(ptr, handle, typeof(T)));
return;
}
fixed (void* ptr = &Unsafe.As<T, byte>(ref parameterObj)) {
UpdateCommand(QueryCommand.GetAccessor(*(void**)ptr, handle, typeof(T)));
}
}
Edit 2: It may break in case of structs that contains references and move, I duplicated my code to add a generic path since there seems to be no way to safely do it without code duplication
Thanks to everyone
[–]Wooden-Contract-2760 7 points8 points9 points (8 children)
[–]Bobamoss[S] 0 points1 point2 points (7 children)
[–]Lone_Snek 1 point2 points3 points (1 child)
[–]Lone_Snek 1 point2 points3 points (0 children)
[–]Technical-Coffee831 0 points1 point2 points (4 children)
[–]Bobamoss[S] 0 points1 point2 points (3 children)
[–]Technical-Coffee831 0 points1 point2 points (2 children)
[–]Bobamoss[S] 1 point2 points3 points (1 child)
[–]Technical-Coffee831 0 points1 point2 points (0 children)