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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions