2013/11/14

C# Tips3 C#からC++を実行しよう

1. C++/CLIとC#の連携
C++/CLIとは、.NET上で実行するプログラムを作るために拡張した言語です。
ためしにC++/CLIでクラスを定義し、C#から呼び出してみましょう。

(1) 新しいプロジェクトを選ぶ
(2) Visual C++のなかのCLRを選び、クラスライブラリを選択。
(3) 名前を決め、OKを押す。
(4) test.cとtest.hは消して構いませんが、他は弄らない方がいいです。
まずはtest.h内に次のように書きます。注意点はpublicとrefを付ける点です。

namespace test {
    public ref class Test {
    public:
        int x;
        Test(int x);
        int getX();
    };
}

test.cpp内には次のように書きます。

#include "stdafx.h"
#include "test.h"

using namespace test;
using namespace std;

Test::Test(int x) {
    this->x = x;
}

int Test::getX() {
    return x;
}
(5) ビルドします。
(6) 新しいプロジェクトでC#のプロジェクトを作ります。
(7) 参照に(5)でできたdllファイルを追加します。/test/Debug/test.dllにあるはずです。
(8) 次のようにしてテストします。

using System;
using test;

class Program {
    public static void Main() {
        Test t = new Test(10);

        Console.WriteLine("{0}", t.getX());
    }
}





2. 配列の受け渡し
数値ができたら次は配列を受け渡してみましょう。

C#側から次のようにしてテストします。
using System;
using test;

class Program {
    public static void Main() {
        Test t = new Test(new int[] { 1, 2, 3 });
        Console.WriteLine(t.m[2]);
        Console.WriteLine(t.sum());
    }
}

C++/CLI側は次のように書きます。

ヘッダ :
#include <string>

namespace test {
    public ref class Test {
    public:
        array<int>^m;

        Test(array<int>^ary);
        int sum();
    };
}

ソース :
#include "stdafx.h"
#include "test.h"

using namespace test;

Test::Test(array<int>^ary) {
    this->m = ary;
}

int Test::sum() {
    int s = 0;

    for each (int var in m){
        s += var;
    }

    for(int i = 0; i < m->Length; i++) {
        s += m[i];
    }

    return s;
}

配列の宣言は
array<int>^変数名;
とします。気持ち悪い^がありますが、これはCLIがガベージコレクションを実装しているからです。
そのため、C++のクラスと別にメモリを管理するためスタックに置かないという意味で^を付けます。
逆に付けないとエラーになります。

配列はfor eachを使ってC#風(VB風?)に記述できます。
もちろん、for文で繰り返すこともできます。
配列の長さも取得できます。なお、array<T>は参照型と呼ばれており、C++でいうポインタなので
プロパティやメソッドを呼び出す際は->とします。

なお、C++/CLIの詳細即ち言語仕様については翻訳版を公開して下さっている方がいるので、
http://vene.wankuma.com/ecma372/StartingState.aspx
を参照して下さい。ただ、非常に大きな言語仕様ですので把握するのは難しいと思います。

C#とC++/CLIを対比させて解説しているページがあります。次を参照して下さい。
http://msdn.microsoft.com/ja-jp/library/cc440828.aspx

3. 文字列の受け渡し
配列と特に変わった点はなく、
String^ str;
などと宣言します。

C++のstd::stringから変換する場合は、
std::string str = "hello";
String^ new_str = gcnew String(str.c_str());
とします。

4. 既存のC++ライブラリ(.libや.dll)をC#から読み出す
これは今までのことができれば、それを応用してできます。
即ち、C#からC++/CLIで作ったライブラリを読み出し、C++/CLIからC++のライブラリを読み出せばいいのです。

5. TPIPWrapper
結局、なんとか引数をC++に変換して使えるようにすることでC#からでも実行できるようです。

例えばTJPTは次のようにしてみました。
TPJT::TPJT(String^ ipaddress, HWND hwnd) {
     char str[32];
     sprintf(str, "%s", ipaddress);
     ::TPJT_init_ex(str, hwnd, 0x03);
}

本当は文字列をString^からchar*に変換するには
http://msdn.microsoft.com/ja-jp/library/d1ae6tz5.aspx
などとしっかりした方法で行うべきです。

いずれにしてもめんどくさいですねえ。

0 件のコメント:

コメントを投稿