Skip to content

Can it wrap native class (methods)? #2

@Superbelko

Description

@Superbelko

I've seen this library was mentioned somewhere on D forums in a fashion like it can be used to wrap native class.

So my questions:
Is it possible to wrap unmanaged class?
And what about allocations?
How to destroy objects?
Is there a better way to wrap this code?

My use case is basically wrap some D code (mix of classes/structs/free functions) to use in C#, possibly extend with inheritance.

Code:

I've slapped some basic test code, on D side there is a simple C++ class with 3 virtual and 1 final methods and no members, there is also factory function to create this one, this class is then loaded on .NET side and called directly using delegates.

// dtest.d
// don't forget to add dependency `dub add mir-algorithm`
import std.stdio;

import mir.utility;
import mir.rc;

export extern(C++) class MyClass 
{
    export void doA() { writeln("hello"); }
    export int doB() { return 42; }
    export int doC(int val) { return 2*val; }
    export final float doFinal() { return 1.5; }

    final ~this() { writeln("~this() called"); }

    pragma(msg, doA.mangleof);
    pragma(msg, doB.mangleof);
    pragma(msg, doC.mangleof);
    pragma(msg, doFinal.mangleof);
}

export extern(C) RCPtr!MyClass makeObj() {
    writeln("makeObj()");
    return createRC!MyClass();
}

version(BuildLibrary)
{
    version(Windows)
    {
        import core.sys.windows.windows;
        import core.sys.windows.dll;
        mixin SimpleDllMain;
    }
}
else
{
    void main()
    {
        auto c = createRC!MyClass();
        c.doA();
    }
}

And the followind C# code, it kind of works and I can wrap it further with some scripting so it can be inherited

// app.cs
// dependencies: `dotnet add package mir`
using System;
using System.Runtime.InteropServices;

using Mir;
using Handle = Mir.Native.Handle;

namespace classtest
{
    public interface MyClass
    {
        [DllImport("dtest", CallingConvention = CallingConvention.Cdecl)]
        public static extern Handle.RCPtr makeObj();

        [DllImport("dtest", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?doA@MyClass@@UEAAXXZ")]
        public static extern void _doA(Handle.RCPtr _this);

        [DllImport("dtest", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?doB@MyClass@@UEAAHXZ")]
        public static extern int _doB(Handle.RCPtr _this);

        [DllImport("dtest", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?doC@MyClass@@UEAAHH@Z")]
        public static extern int _doC(Handle.RCPtr _this, int val);

        [DllImport("dtest", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?doFinal@MyClass@@QEAAMXZ")]
        public static extern float _doFinal(Handle.RCPtr _this);
    }

    
    class Program
    {
        static void Main(string[] args)
        {
            var cls = MyClass.makeObj();

            if (cls.Ptr == IntPtr.Zero)
                throw new NullReferenceException("makeObj failed");

            MyClass._doA(cls);

            var b = MyClass._doB(cls);
            Console.WriteLine(b);

            var c = MyClass._doC(cls, 4);
            Console.WriteLine(c);

            MyClass._doFinal(cls);
            var f = MyClass._doFinal(cls);
            Console.WriteLine(f);
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions