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でどうなるかだな。。。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です