bottlenoselabs / c2cs

Generate C# bindings from a C header.
MIT License
250 stars 18 forks source link

Improve type aliases for function pointers #151

Open shayded-exe opened 1 year ago

shayded-exe commented 1 year ago

I'm trying to generate bindings for Nuklear.

It declares the following function pointer: https://github.com/Immediate-Mode-UI/Nuklear/blob/a14e55f536e0d582930b68b2d3cac7296bae3a25/nuklear.h#L504

typedef nk_bool(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode);

This generates the following C#:

[CNode(Kind = "FunctionPointer")]
[StructLayout(LayoutKind.Sequential)]
public struct FnPtr_NkTextEditPtr_NkRune_CBool
{
    public delegate* unmanaged<nk_text_edit*, nk_rune, CBool> Pointer;
} 

[CNode(Kind = "TypeAlias")]
[StructLayout(LayoutKind.Explicit, Size = 8, Pack = 8)]
public struct nk_plugin_filter
{
    [FieldOffset(0)]
    public FnPtr_NkTextEditPtr_NkRune_CBool Data;

    public static implicit operator FnPtr_NkTextEditPtr_NkRune_CBool(nk_plugin_filter data) => data.Data;
    public static implicit operator nk_plugin_filter(FnPtr_NkTextEditPtr_NkRune_CBool data) => new() { Data = data };
}

It would be nice if the TypeAlias wrapper could be eliminated and the function pointer struct were named using its declared name. Additionally, it should provide a constructor and explicit conversion operator for ease of use. Like this:

[CNode(Kind = "FunctionPointer")]
[StructLayout(LayoutKind.Sequential)]
public struct nk_plugin_filter
{
    public delegate* unmanaged<nk_text_edit*, nk_rune, CBool> Pointer;

    public nk_plugin_filter(delegate* unmanaged<nk_text_edit*, nk_rune, CBool> pointer) {
        this.Pointer = pointer;
    }

    public static explicit operator nk_plugin_filter(delegate* unmanaged<nk_text_edit*, nk_rune, CBool> pointer) {
        return new(pointer);
    }
} 
lithiumtoast commented 1 year ago

Thanks @shayded-exe for the suggestion.

I was experimenting with this before. The main problem was that wrapping a function pointer in a struct is useful for the case when using delegates for backwards compatibility before .NET 5. I was lazy so I left it wrapped.

You can take a look at the generator class with uses Roslyn to create the struct: https://github.com/bottlenoselabs/c2cs/blob/925e3ae18d223394858853e8a57d3907388ba32b/src/cs/production/C2CS.Tool/Features/WriteCodeCSharp/Domain/CodeGenerator/Handlers/FunctionPointerCodeGenerator.cs#L4

shayded-exe commented 1 year ago

Ah, makes sense

I've switched to ImGui for now so I can make progress faster, so I'm not using this tool for now, but when I inevitably need bindings for another library I'll make a fork of this to make improvements on. I've searched around a lot and this is so far the best/easiest option for generating bindings that I've found! If I need to pick it back up I'm happy to contribute.