Monoを組み込み処理系として使う
Monoの言語(C#、Boo、etc.)をスクリプティングとして使う場合とかに使える、C++の小さいサンプルを作ってみた。
スクリプティングとして使う場合は「C++→スクリプト」と「スクリプト→C++」の2方向の呼び出しができる必要がある。ということで最小限それができるサンプルを。
「C++→スクリプト」はMonoのAPI (mono_runtime_invokeとか) をつかってがんばる。「スクリプト→C++」はP/Invokeを使えばできるらしい。とりあえずやってみる。
試験環境はGentoo Linux、Mono 2.8.2です。monotest.cppがメインで、test.dllを呼び出している。test.dllはBoo言語でかかれている。ちなみにBoo言語はPythonライクなMonoの言語。まぁべつにC#でつくっても動くはず。。。
monotest.cpp:
#include <mono/jit/jit.h> #include <mono/metadata/assembly.h> #include <mono/metadata/debug-helpers.h> #include <string> #include <iostream> #include <stdlib.h> #include <stdio.h> #include <dlfcn.h> using namespace std; extern "C" { extern void PrintFunc(int val); } void error(const string &msg = "") { cerr << "Error:" << msg << endl; abort(); } int main(int argc, char* argv[]) { // Export Check void *ThisModule = dlopen(NULL, RTLD_LAZY); cout << "Module Handle:" << ThisModule << endl; void *pFunc = dlsym(ThisModule, "PrintFunc"); cout << "Function Handle:" << pFunc << endl; if(!pFunc) error("No symbol: PrintFunc"); MonoDomain *domain; const char *file; int retval; if (argc < 2){ fprintf (stderr, "Please provide an assembly to load\n"); return 1; } file = argv [1]; domain = mono_jit_init("monotest"); MonoAssembly *assembly = mono_domain_assembly_open (domain, "test.dll"); if (!assembly) error(); PrintFunc(0); MonoImage *image = mono_assembly_get_image(assembly); MonoClass *klass = mono_class_from_name(image, "TestNamespace", "TestClass"); MonoMethodDesc *desc = mono_method_desc_new(":TestStaticMethod(int)", false); MonoMethod *static_method = mono_method_desc_search_in_class (desc, klass); void *args[1]; int val1 = 1234; args[0] = &val1; mono_runtime_invoke (static_method, NULL, args, NULL); mono_jit_cleanup (domain); return 0; } void PrintFunc(int val) { cout << "In PrintFunc :" << val << endl; }
test.boo:
namespace TestNamespace import System.Runtime.InteropServices [DllImport("__Internal", EntryPoint: "PrintFunc")] def PrintFunc(val as int): pass class TestClass: static def TestStaticMethod(val1 as int): print "In TestClass: ", val1 PrintFunc(val1 * 2) return
コンパイル:
$ g++ -export-dynamic -g monotest.cpp -o monotest `pkg-config --cflags --libs mono-2` $ booc test.boo
実行結果:
$ ./monotest test.dll Module Handle:0x46d048e0 Function Handle:0x8048ff0 In PrintFunc :0 In TestClass: 1234 In PrintFunc :2468
P/Invokeはちょっと注意が必要。たぶんdlopenで自分自身を開いてやってるので、シンボルを参照できるようにする必要がある。具体的には、関数をextern “C”する必要があったり、コンパイル時に-export-dynamicをつける必要があったり。monotest.cppのmainの頭についているdlopenのテストコードはそれのチェック用。もしかしたらこれは外部ライブラリとかにしたほうがいいのかもね。
まぁとりあえずそれなりに簡単にMonoを組み込むことができそうということが分かった。
あとはWindowsでどうなるかだな。。。