aki.の月記 このページをアンテナに追加 RSSフィード

2006 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 12 |
2008 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 03 | 04 | 05 |

2007-03-13

[][][] C++/CLI + mono その2  C++/CLI + mono その2を含むブックマーク  C++/CLI + mono その2のブックマークコメント

http://d.hatena.ne.jp/ak11/20070306/p1

の続き。

結局のところ、CRTさえ使わなければ、C++/CLIのコードはmonoで動かすことが出来るようだ。

↓こんな感じのコードを書いて、 コンパイルオプションに/clr:pureと、リンクオプションに/nodefaultlib、/entry:mainを指定するのが基本となる。

int main(array<System::String^>^ args) {
}
#pragma warning(disable:4483)
void __clrcall __identifier(".cctor")() {}

とはいえ、C++/CLIに限った話ではないが、VC++において、CRTを使用しないというのは、結構こまごまと制約がある。C/C++の標準関数STLなどが(ほとんど)使えないのは当然だが、他にも、グローバル変数(静的変数)の動的な初期化や、vtable絡みなどが引っかかる。

グローバル変数は元々多用するものでも無く、回避も簡単なので問題ないが、vtableの方は少し面倒。具体的には、vtableを持つクラス(1個以上の仮想関数を持つクラス)のコンストラクタやデストラクタの定義がC++/CLI側から見えてはいけない。

例えば、インターフェースクラスの宣言などでこんな書き方をする場合があるが、

class IHoge {
public:
    virtual ~IHoge() {} // ここがダメ。
    virtual void Hoge() = 0;
};

これをC++/CLI側から#includeしたりすると、それだけでリンクエラーがいくつか出てしまう。C++標準ライブラリのヘッダなどを#includeしたときもこれに引っかかる事があるので注意が必要だ。

コンストラクタやデストラクタは、 virtual ~IHoge(); のように定義を伴わない宣言だけを書かなくてはならない。単にこれだとC++側に定義が必要になってしまうので、__cplusplus_cli などを利用して切り替えるといいかもしれない。

そして、それに絡むが、ネイティブのクラスを継承して仮想関数をオーバーライドする事が出来ない。そもそも/clr:pureを指定しているので、__clrcall以外の(__cdeclなどの)関数は定義出来ないのだが。

ただし、関数ポインタであれば、System::Runtime::InteropServices::UnmanagedFunctionPointerAttribute と System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate を使うことで何とか作り出せる。

あとは、new/deleteも、使うなら自前で定義する必要がある。これはまぁ、System::Runtime::InteropServices::Marshal::AllocHGlobalでも使って適当に定義するだけでいいのだが。

void* operator new(size_t n) {
    return (void*)System::Runtime::InteropServices::Marshal::AllocHGlobal((int)n);
}
void operator delete(void* p) {
    if (p == 0) return;
    System::Runtime::InteropServices::Marshal::FreeHGlobal(System::IntPtr(p));
}

あと、これはあまり検証していないが、名前マングリングの関係で、ネイティブのDLLからimportする関数には、extern "C" が付いて無いと、monoで動かしたときにEntryPointNotFoundExceptionが発生するようだ。

__declspec(dllexport)によるクラスのエクスポートも多分動かないだろう。

トラックバック - http://d.hatena.ne.jp/ak11/20070313/p1