CppSharpへのいざない


https://github.com/mono/CppSharp

## CppSharpとは
CppSharpとは、C++で実装されたライブラリやアプリケーションの.NETラッパーを作成するためのツールです。今までは手作業でラッパーのコードを書いていましたが、このツールを使うことにより簡単にラッパーを作成することができます。

ただし、手作業での作成ではライブラリの使い方にそった最適な変換方法を選択できるの対し、自動でのラッパー作成は一般的な変換方法に則っています。つまり、パフォーマンスやライブラリの質の面で手作業より不利となる可能性があります。

類似のプロジェクトとして以下があります。
- Sharppy - .NET bindings generator for unmanaged C++ https://code.google.com/p/sharppy/
- XInterop http://xinterop.com/
- SWIG http://www.swig.org/
- Cxxi https://github.com/mono/cxxi/

## .NETライブラリのラッパー
Cppで実装されたネイティブコードを.NETのマネージドで使うための方法は、PInvokeまたはC++/CLIがあります。CppSharpではどちらにも対応しますが、今回のいざないでは、C++/CLIで説明します。というのも、私の環境ではPInvokeの作成に成功しなかったからです。

ちなみにPInvokeは実行時にネイティブコードとのリンクを行なうのに対し、C++/CLIではビルド時にリンクを行なうといった違いがあります。また、C++/CLIの方がパフォーマンスがいいようです。

## CppSharpのダウンロード
CppSharpは現在開発中のため、いまだ安定版のリリースはありません。しかし、一応ビルドされたものを公開してあります。自分でビルドするのは大変なので、ビルドされたものをありがたく使わしてもらいましょう。なお、このビルドされたDLLは32ビット用です。

リリースの中の最新版をダウンロードしましょう。
https://github.com/mono/CppSharp/releases

![スクリーンショット 2016-01-03 20.37.27.png](https://qiita-image-store.s3.amazonaws.com/0/105381/3046888a-0b89-9fcb-55bc-e2a1eb761ef0.png)

## ライブラリを作ってみる
では、実際にラッパーライブラリを作りましょう。

手順は以下のとおりです。
1. ラップする対象のライブラリをC++で作成
2. ラップしてくれるソフトウェアを作り、変換規則を記述
3. 2のソフトウェアでラップ用のソースコードを生成
4. 3のソースコードでラッパーライブラリをC++/CLIで作成
5. ラッパーライブラリのDLLをビルド
5. アプリケーションを作成し、DLLを参照、ラップされたことを確認

ちなみに私の作業環境は、Visual Studio 2015 Communityです。

### 1. ラップする対象のライブラリをC++で作成
まず、Visual Studio でC#のコンソールアプリケーションを新しいプロジェクトとしてソリューションを新しく作成します。名前はMyCppSharpとしておきます。

次に、新しいプロジェクトをC++の空のプロジェクトで作成します。名前はSampleとしてください。

Fooクラスを作成し、ファイルの中身を以下のようにしてください。

```cpp:Foo.h
class Foo
{
public:

	int a;
	float b;
};

int FooAdd(Foo* foo);
```

```cpp:Foo.cpp
#include "Foo.h"

int FooAdd(Foo* foo) {
	return foo->a + (int)foo->b;
}
```

Sampleプロジェクトのプロパティで、構成の種類をスタティックライブラリに変更してください。

![スクリーンショット 2016-01-03 20.59.09.png](https://qiita-image-store.s3.amazonaws.com/0/105381/d5fa6584-ea89-83d4-27ae-9839545cdecb.png)

Sampleプロジェクトをビルドします。

### 2. ラップしてくれるソフトウェアを作り、変換規則を記述
ソリューション作成時に一緒に作成されたMyCppSharpプロジェクトを開きます。参照を追加します。ダウンロードしたCppSharpの中に含まれているDLLの中から以下を選択してください。

- CppSharp.AST.dll
- CppSharp.dll
- CppSharp.Generator.dll
- CppSharp.Parser.CLI.dll
- CppSharp.Parser.CSharp.dll
- CppSharp.Runtime.dll

次に、以下のC#コードを作成してください。

```csharp:Program.cs
using CppSharp;

namespace MyCppSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            ConsoleDriver.Run(new SampleLibrary());
        }
    }
}
```

```csharp:SampleLibrary.cs
using CppSharp;
using CppSharp.AST;
using CppSharp.Generators;

namespace MyCppSharp
{
    class SampleLibrary : ILibrary
    {
        public void Postprocess(Driver driver, ASTContext ctx)
        {
        }

        public void Preprocess(Driver driver, ASTContext ctx)
        {
        }

        public void Setup(Driver driver)
        {
            var options = driver.Options;
            options.GeneratorKind = GeneratorKind.CLI;
            options.LibraryName = "Sample";
            options.Headers.Add("Foo.h");
            options.Libraries.Add("Sample.lib");
            options.addIncludeDirs("../../../Sample");
            options.addLibraryDirs("../../../Debug");
        }

        public void SetupPasses(Driver driver)
        {
        }
    }
}
```

### 3. 2のソフトウェアでラップ用のソースコードを生成
さて、MyCppSharpプロジェクトのビルドは成功しますが、実行すると以下のエラーがでます。

```
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in MyCppSharp.exe

Additional information: ファイルまたはアセンブリ 'CppSharp.Parser.CLI.dll'、またはその依存関係の 1 つが読み込めませんでした。指定されたモジュールが見つかりません。
```

依存関係とかよくわからないので、ダウンロードしたファイルをすべて、`MyCppSharp/bin/Debug`に上書きコピーしましょう。

今度は実行に成功し、実行フォルダに`Sample.cpp`と`Sample.h`が生成されます。

### 4. 3のソースコードでラッパーライブラリをC++/CLIで作成
新しいプロジェクトを、C++の空のプロジェクトとして作成します。名前はManagedSampleとしておきます。

さきほど作成した2つのソースコードをプロジェクトにインポートしてください。一度`ManagedSample`フォルダ直下にコピーしてから、ソリューションエクスプローラーのManagedSampleプロジェクトにドラッグ&ドロップするほうがいいと思います。

また、`CppSharp.h`を以下からコピーして作成してください。
https://github.com/mono/CppSharp/blob/master/include/CppSharp.h

プロジェクトのプロパティを開き、以下のように変更してください。

![スクリーンショット 2016-01-03 21.25.09.png](https://qiita-image-store.s3.amazonaws.com/0/105381/12d8b9df-5ffb-fa9a-d006-d6288c85ca95.png)
![スクリーンショット 2016-01-03 21.27.22.png](https://qiita-image-store.s3.amazonaws.com/0/105381/1af59806-314c-d8df-1983-059f932f7371.png)
![スクリーンショット 2016-01-03 21.28.32.png](https://qiita-image-store.s3.amazonaws.com/0/105381/5e14b678-d955-b825-c708-919b4840675b.png)
![スクリーンショット 2016-01-03 21.29.08.png](https://qiita-image-store.s3.amazonaws.com/0/105381/9648fa1b-3add-348a-e709-4ad53cbdd149.png)

### 5. ラッパーライブラリのDLLをビルド
ManagedSampleプロジェクトをビルドすると、`Debug/ManagedSample.dll`が生成されています。

### 6. アプリケーションを作成し、DLLを参照、ラップされたことを確認
新しいプロジェクトを、C#のコンソールアプリケーションで作成します。名前はMyAppとします。

参照の追加で、先ほど生成した`ManagedSample.dll`を参照に追加します。また、スタートアッププロジェクトにしておきます。

プログラムを以下のようにしましょう。

```csharp:Program.cs
using System;
using Sample;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var foo = new Foo();
            foo.a = 1;
            foo.b = 2.3f;
            Console.WriteLine(Foo.FooAdd(foo));
        }
    }
}
```

実行すると`3`と表示されました。

## P/Invoke

先はC++/CLIによるラッパーを作成しました。今度はP/Invokeによるラッパーを作成します。

### ネイティブDLLの作成

Sampleプロジェクトのファイルに`DllExport`を追加します。

```cpp:Foo.h
#pragma once
#define DllExport __declspec( dllexport )
class DllExport  Foo
{
public:
	int a;
	float b;
};
int DllExport FooAdd(Foo* foo);
```

DLL内の定義を外部から参照可能にするには、`DllExport`で修飾する必要があります。

また、生成されるファイルは`.lib`でなく`.dll`でなければならないので、プロジェクト構成から変更してください。

### CppSharpでCSファイルの生成

MyCppSharpプロジェクトで生成するファイルのオプションを変更します。`SampleLibrary.cs`を以下のようにしてください

```csharp::SampleLibrary.cs
        public void Setup(Driver driver)
        {
            var options = driver.Options;
            options.GeneratorKind = GeneratorKind.CSharp;
            options.LibraryName = "Sample";
            options.Headers.Add("Foo.h");
            options.Libraries.Add("Sample.dll");
            options.addIncludeDirs("../../../Sample");
        }
```

実行すると`Foo.cs`ファイルが生成されます。

### アプリケーションでライブラリを呼び出す

生成されたDLLファイルとCSファイルを、ライブラリを利用するアプリケーションのプロジェクトにコピーします。あとは、先と同じように使えます。DLLファイルは、ビルドされたEXEファイルと同じ場所にコピーされるようにしておいてください。

## まとめ
CppSharpへのいざないとして、自動的にC++のコードをラップするツールに触れました。今回は簡単なクラスを作成しラップしましたが、実際のソフトウェアは配列や文字列などの扱いから複雑です。また、メモリ解放について気をつけるべきなのですが、どのようになっているのかはまだ理解していません。そのへんもいつか記事にできたらなと思います。

*fin*