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

Can it wrap native class (methods)? #2

Open
Superbelko opened this issue Nov 29, 2020 · 4 comments
Open

Can it wrap native class (methods)? #2

Superbelko opened this issue Nov 29, 2020 · 4 comments

Comments

@Superbelko
Copy link

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);
        }
    }
}
@9il
Copy link
Member

9il commented Dec 2, 2020

Is it possible to wrap unmanaged class?

Yes

And what about allocations?

They are automatic (and require D creation function for classes but not for structs). And looks simply like new MyClass on C# side.

How to destroy objects?

They are destroyed when the reference count is decreased to 0. The reference count is decreased when the C# object is finalized.

Is there a better way to wrap this code?

Yes. It is a bit buggy. For example, extern(C) function shouldn't return ref-counted structs/objects. Also the class C# should be inherited from MirPtr or MirSlimPtr.

I will rework it at the end of this week and add it to the CI.

@Superbelko
Copy link
Author

Ok, thanks. I'll try to mess a bit with existing code, but having an example for reference certainly will be helpful.

@9il
Copy link
Member

9il commented Dec 7, 2020

@Superbelko Do you need an example with inheritance support or plain pointers?

@Superbelko
Copy link
Author

As you've mentioned earlier, simply deriving from MirWrapper seems to make factory function unnecessary(or I could be totally wrong since in such basic example there is no internal state that could mess up with ABI), however I haven't figured out myself how to call methods on wrapper implementation.

So it would be awesome if you provided some demo case with native classes, including methods and data access.

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

No branches or pull requests

2 participants