Reputation: 163
I need some way of constructing a function pointer type that points to a specific function signature. So something like this:
Type[] args = [typeof(int), typeof(int)];
// Pointer to (int -> int) method
Type functionPointerType = typeof(delegate*<>).MakeGenericType(args);
The above sadly does not work, as the type arguments for delegate*<>
need to be fully specified. I looked through the documentation and could not find anything regarding this.
A more specific example, how would I use System.Reflection.Emit
to create a field such as this:
.field public method int32 *(int32) function
Upvotes: 3
Views: 80
Reputation: 117046
As long as the total number of arguments you might need has some upper bound (say 16 as you mentioned in comments), you could manufacture your function pointer types inside some generic method you invoke via MakeGenericMethod()
. For instance, if you first create the following extension methods:
public static class FunctionPointerExtensions
{
public static Type MakeGenericManagedCallFunctionPointerType(this (Type returnType, Type [] argumentTypes) types)
{
if (!(GetGenericMethod(types.argumentTypes.Length + 1) is {} genericMethod))
throw new ArgumentException($"Generic arguments of length {types.argumentTypes.Length} are not supported");
return (Type)genericMethod.MakeGenericMethod(types.argumentTypes.Concat(new [] { types.returnType }).ToArray()).Invoke(null, null); // Remember TReturn is the LAST generic parameter
}
public static Type MakeGenericManagedCallVoidFunctionPointerType(this Type [] argumentTypes)
{
if (argumentTypes.Length == 0)
return typeof(delegate* managed<void>);
if (!(GetGenericMethod(argumentTypes.Length) is {} genericMethod))
throw new ArgumentException($"Generic arguments of length {argumentTypes.Length} are not supported");
return (Type)genericMethod.MakeGenericMethod(argumentTypes).Invoke(null, null);
}
static MethodInfo? GetGenericMethod(int length, [CallerMemberName] string memberName = "") =>
typeof(FunctionPointerExtensions).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
.Where(m => m.Name == memberName && m.IsGenericMethod && m.GetGenericArguments().Length == length)
.SingleOrDefault();
// The names of the private methods below should be the same as the name of the corresponding public method to ensure that [CallerMemberName] works
// managed is the default, I used it explicitly for clarity
static Type MakeGenericManagedCallFunctionPointerType<TReturn>() => typeof(delegate* managed<TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, TReturn>() => typeof(delegate* managed<T1, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, TReturn>() => typeof(delegate* managed<T1, T1, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, TReturn>() => typeof(delegate*managed<T1, T1, T3, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TReturn>);
static Type MakeGenericManagedCallFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TReturn>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TReturn>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1>() => typeof(delegate* managed<T1, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2>() => typeof(delegate* managed<T1, T1, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3>() => typeof(delegate*managed<T1, T1, T3, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4>() => typeof(delegate* managed<T1, T1, T3, T4, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5>() => typeof(delegate* managed<T1, T1, T3, T4, T5, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, void>);
static Type MakeGenericManagedCallVoidFunctionPointerType<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>() => typeof(delegate* managed<T1, T1, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, void>);
}
Then, for a function pointer taking two integers with a void
return, you could do:
Type[] args = [typeof(int), typeof(int)];
// Pointer to (int, int) -> void method
var functionPointerType = args.MakeGenericManagedCallVoidFunctionPointerType();
Or if you want a function pointer taking an int and returning an int (it wasn't clear from your question which you wanted), you could do:
Type[] args = [typeof(int)];
// Pointer to int -> int method
var functionPointerType = (typeof(int), args).MakeGenericManagedCallFunctionPointerType();
Notes:
Because we cannot use void
as a generic parameter we need to handle the case of a function pointer with no return separately from the case of a function pointer that returns something.
Unlike Action<T1, ... >
and Func<T1, ...>
, I could not find any limit on the length of a function pointer parameter list. That may explain why there are no open generic types for function pointer types, as open generic types must have a fixed number of parameters.
Even with Action<T1,..., TN>
you can't do typeof(Action<>).MakeGenericType(args);
to manufacture a closed generic type with the number of generic parameters known only at runtime. There is no C# syntax currently that allows you to specify an open generic with a variable number of generic parameters[1].
You would need separate methods for when some or all arguments are passed by reference rather than by value, and for different calling conventions.
As an alternative, I suppose you could look into using TypeBuilder
to manufacture a factory that manufactures the required type for any number of generic parameters specified at runtime.
Demo fiddle here.
Upvotes: 3