C#からC++を呼び出してポインタを使う

C#からC++を呼び出す方法を調べていたのですが、ポインタつかってごにょごにょできることを知って驚きました。 安全に使うには当然いろいろと気をつけなくてはいけないのですが、C++ではここまでできるのかと。

ソースコード

lib.hの記述

class Value
{
public:
    int v;
};

typedef void *Handle;
extern "C" int CreateValue(Handle *out, int x);
extern "C" int AddValue(Handle handle, int x);
extern "C" int GetValue(Handle handle, int *out);

lib.cppの記述

#include "lib.h"

int CreateValue(Handle *out, int v) {
    *out = new Value{v};
    return 0;
}

int AddValue(Handle handle, int v) {
    auto p = static_cast<Value*>(handle);
    p->v += v;
    return 0;
}

int GetValue(Handle handle, int *out) {
    auto p = static_cast<Value*>(handle);
    *out = p->v;
    return 0;
}

main.csの記述

ポイントは以下のとおり:

  • DllImportでC++ライブラリとのインターフェースを定義する
  • IntPtr型でC++のポインタを扱う
using System;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        External.CreateValue(out var ptr, 10000);
        int v;
        External.GetValue(ptr, out v);
        Console.WriteLine(v);
        for (int i = 0; i < 5; i++)
        {
            External.AddValue(ptr, 100);
            External.GetValue(ptr, out v);
            Console.WriteLine(v);
        }
        // おそらくメモリリークしている
    }
}

static class External
{
    private const string Path = "../../../lib.so";

    [DllImport(Path)]
    public static extern int CreateValue(out IntPtr ptr, int v);

    [DllImport(Path)]
    public static extern int AddValue(IntPtr ptr, int v);

    [DllImport(Path)]
    public static extern int GetValue(IntPtr ptr, out int v);

}

参考文献

アンマネージ コードとの相互運用

docs.microsoft.com

xgboostのAPI

github.com

実際ちゃんと使うのであれば、xgboostが行っているようにさまざまな用心をする必要がありそうです。