MochiLibraries / Biohazrd

A framework for automatically generating binding wrappers for C/C++ libraries
MIT License
61 stars 10 forks source link

Improve friendliness of constructing/destructing C++ objects #161

Open PathogenDavid opened 3 years ago

PathogenDavid commented 3 years ago

Right now Biohazrd emits constructors and destructors as normal instance methods, they both act as if they would for placement new.

This leads to somewhat clunky usage for allocating objects from C#. For example:

struct MyStruct
{
public:
    float x;
    float y;
    MyClass(float x, float y);
    ~MyClass();
};

Will generate something like:

[StructLayout(LayoutKind.Explicit, Size = 8)]
public unsafe partial struct MyStruct
{
    [FieldOffset(0)] public float x;
    [FieldOffset(4)] public float y;

    [DllImport("Native.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "__InlineHelper0", ExactSpelling = true)]
    private static extern void Constructor_PInvoke(MyStruct* @this, float x, float y);

    [DebuggerStepThrough, DebuggerHidden]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public unsafe void Constructor(float x, float y)
    {
        fixed (ImVec2* @this = &this)
        { Constructor_PInvoke(@this, x, y); }
    }

    [DllImport("InfectedImGui.Native.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "__InlineHelper1", ExactSpelling = true)]
    private static extern void Destructor_PInvoke(MyStruct* @this);

    [DebuggerStepThrough, DebuggerHidden]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public unsafe void Destructor()
    {
        fixed (MyStruct* @this = &this)
        { Destructor_PInvoke(@this); }
    }
}

"Proper" usage of this type in a stack-allocated context looks like this:

MyStruct myStruct = default;
myStruct.Constructor(100f, 200f);
Console.WriteLine($"myStruct = {myStruct.x}, {myStruct.y}");
myStruct.Destructor();

Ideally we'd like it to be able to look like this:

using (MyStruct myStruct = new(100f, 200f))
{ Console.WriteLine($"myStruct = {myStruct.x}, {myStruct.y}"); }

Other things we should ideally support:

Other things to ponder:

PathogenDavid commented 3 years ago

It's somehow not mentioned above, but parameterless constructors are coming to C# 10 so that solves the main reason I hadn't actually done this yet. Julien Couvreur confirmed they should land in 2022p3: https://github.com/dotnet/roslyn/pull/54359#issuecomment-868837007