Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# should provide better interop with unmanaged function pointers. #13240

Closed
tannergooding opened this issue Aug 18, 2016 · 4 comments
Closed

Comments

@tannergooding
Copy link
Member

Issue
C# has no mechanism for declaring unmanaged function pointers.

While there are a couple of mechanisms designed to work with and call unmanaged function pointers, these mechanisms may:

  • Incur a higher level of overhead
  • Require caching of the managed delegate
  • Emit sub-optimal instructions for invoking the unmanaged function pointer
  • Have issues when working with other unsafe/unmanaged code

Workaround
Declare the function pointer using IntPtr and then use the Marshal.GetDelegateForFunctionPointer method to convert to a managed Delegate. Such as:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void _D();

public struct S
{
    private IntPtr pD;

    public void D()
    {
        var funcptr = Marshal.GetDelegateForFunctionPointer<_D>(pD);
        funcptr();
    }
}

public static class C
{
    [DllImport("SomeBinary.dll")]
    public static extern void CreateS(out S pS);
}

-or-

Declare a managed delegate and rely on the marshalling behavior to convert as appropriate. Such As:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void _D();

public struct S
{
    [MarshalAs(UnmanagedType.FunctionPtr)]
    public _D D;
}

public static class C
{
    [DllImport("SomeBinary.dll")]
    public static extern void CreateS(out S pS);
}

Proposal
The C# language should be extended to support function pointers in an unsafe context. Such as:

unsafe public struct S
{
    public void (*D)();
}

The compilation should fail if unsafe is missing.

Advantages
The compiler could take advantage of the fact this is an unsafe function pointer and emit the calli instruction when invoking.

The compiler could provide some level of type safety when invoking the function pointer (ensuring the arguments passed in match the types required).

This would allow better interop with unmanaged languages and types that use function pointers.

This would allow a user to more readily implement VTBLs for unmanaged interop when required.

@gafter
Copy link
Member

gafter commented Aug 18, 2016

@jaredpar I think this is related to something you've been working on.

@tannergooding
Copy link
Member Author

I talked with @jaredpar briefly and he had some ideas on how we could do this in a 'safe' manner (thus removing the requirement for 'function pointer' support to only be allowed in an unsafe code block).

I'll let him add his thoughts on the matter (or will add a brief detailing of our discussion if he gives me the go-ahead)

@spellizzari
Copy link

This feature would be welcome by projects such as SharpDX that makes its own COM wrappers for lots of Windows APIs (currently it uses the calli instruction by patching the MSIL code of its assemblies after the compilation).

While searching about this topic I saw a post by Eric Lippert on StackOverflow http://stackoverflow.com/questions/36558996/how-to-implement-c-style-function-pointers-in-c-without-using-delegates where he explains the difficulties of finding the right syntax.

One workaround I can think of is to make for delegates the same thing that is currently in place for structs in an unsafe context: distinguish between blittable and non-blittable types.

In an unsafe context I can use MyStruct* for fields, parameters and local variables as long as MyStruct is a blittable type. If I attempt to use pointers to managed structs I get an error at compile time.

Maybe the solution would be to:

  • allow MyDelegate* for fields, parameters and local variables where MyDelegate would be a classic delegate definition, and produce compilation errors when MyDelegate contains non-blittable parameters or parameters requiring marshalling.
  • use the existing UnmanagedFunctionPointerAttribute attribute to specify the calling convention directly on the delegate.
  • allow the method call syntax on delegate pointers like they were delegates (that is, if myFuncPtr is a variable of type MyDelegate*, where MyDelegate is delegate void MyDelegate(int p1); I should be able to write myFuncPtr(123)). This would allow the compiler to produce a calli instruction with a safely validated signature.

The main advantage of this solution is that no new syntax is needed for the language, it is only a compiler thing.

@tannergooding
Copy link
Member Author

Ported to dotnet/csharplang#80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants