C++ で正規表現ってどうやるんだっけ? ということで調べていたところ、Visual C++ では STL の regex が使えると ( Visual C++ 2008 以降みたいです )。とりあえず、こんな感じで。
#include "stdafx.h" #include <regex> #include <iostream> using namespace std; typedef std::regex_iterator<const TCHAR *> itr; int _tmain(int argc, _TCHAR* argv[]) { const TCHAR *pat = _T("abc123def456ghi789"); itr::regex_type rx(_T("(\\d+)")); itr next(pat, pat + _tcslen(pat), rx); itr end; for (; next != end; ++next) { _tprintf(_T("[%s] (pos: %d / len: %d)\n"), next->str().c_str(), next->position(), next->length()); } return 0; }
普段、あんまり意識して C++ のコードを書くことはないので、STL とか苦手です。たいていの場合、C の範囲で済ませちゃいますし。いい感じにフォーマットする方法がわからなかったので、今さらながらの printf() と。ベタな方法ではありますが、やっぱり便利ですよね。
JNI によって、他の開発言語から jvm.dll ( C/C++ インターフェイス ) 経由で Java VM を起動することができます。ヘッダが用意されているものの、ポインタの嵐だったりするので、.NET Framework からは C/C++ のラッパー経由で呼ぶ、というのが一般的な模様。
とはいえ、P/Invoke 経由で呼べないこともないだろうと、ちょっと試してみました。その結果、なんとか呼び出すことには成功したものの、確かにえらい面倒。とりあえず、こんな感じです。まずは、構造体やら関数やらの宣言から。
[StructLayout(LayoutKind.Sequential)] public struct JavaVMInitArgs { public int version; public int nOptions; public IntPtr options; public bool ignoreUnrecognized; } [StructLayout(LayoutKind.Sequential)] public struct JavaVM { public IntPtr functions; /* JNIInvokeInterface */ [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate JNIResult DestroyJavaVM( IntPtr vm); } [StructLayout(LayoutKind.Sequential)] public struct JNIInvokeInterface { public IntPtr reserved0; public IntPtr reserved1; public IntPtr reserved2; public IntPtr DestroyJavaVM; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public IntPtr[] __unused1; } [StructLayout(LayoutKind.Sequential)] public struct JNIEnv { public IntPtr functions; /* JNINativeInterface */ [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate int GetVersion( IntPtr env); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate IntPtr FindClass( IntPtr env, [MarshalAs(UnmanagedType.LPStr)] string name); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate IntPtr GetStaticMethodID( IntPtr env, IntPtr clazz, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string sig); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate int CallStaticIntMethodA( IntPtr env, IntPtr clazz, IntPtr methodID, jvalue[] args); } [StructLayout(LayoutKind.Sequential)] public struct JNINativeInterface { public IntPtr reserved0; public IntPtr reserved1; public IntPtr reserved2; public IntPtr reserved3; public IntPtr GetVersion; public IntPtr __unused0; public IntPtr FindClass; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 106)] public IntPtr[] __unused1; public IntPtr GetStaticMethodID; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] public IntPtr[] __unused2; public IntPtr CallStaticIntMethodA; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] public IntPtr[] __unused3; } [StructLayout(LayoutKind.Explicit)] public struct jvalue { [FieldOffset(0)] public bool z; [FieldOffset(0)] public byte b; [FieldOffset(0)] public char c; [FieldOffset(0)] public short s; [FieldOffset(0)] public int i; [FieldOffset(0)] public long j; [FieldOffset(0)] public float f; [FieldOffset(0)] public double d; [FieldOffset(0)] public IntPtr l; } public enum JNIResult { JNI_OK = 0, /* success */ JNI_ERR = (-1), /* unknown error */ JNI_EDETACHED = (-2), /* thread detached from the VM */ JNI_EVERSION = (-3), /* JNI version error */ JNI_ENOMEM = (-4), /* not enough memory */ JNI_EEXIST = (-5), /* VM already created */ JNI_EINVAL = (-6), /* invalid arguments */ } public const int JNI_VERSION_1_6 = 0x00010006; [DllImport("jvm.dll", CallingConvention = CallingConvention.StdCall)] public static extern JNIResult JNI_CreateJavaVM( out IntPtr pvm, out IntPtr penv, ref JavaVMInitArgs args);
最低限の宣言しかありませんし、構造体のメンバも省略してます。必要に応じて、関数やら構造体やらの宣言を追加してください。jvm.dll が公開している関数は、一部のものを除いて関数ポインタとして供給されます。ということで、.NET Framework 側では delegate として扱う必要があります。
static private JavaVM.DestroyJavaVM destroyJavaVM; static private JNIEnv.GetVersion getVersion; static private JNIEnv.FindClass findClass; static private JNIEnv.GetStaticMethodID getStaticMethodID; static private JNIEnv.CallStaticIntMethodA callStaticIntMethodA;
呼び出し部分は、JNI_CreateJavaVM() 関数を使って JavaVM / JNIEnv を取得し、関数ポインタテーブルから必要な部分を delegate に変換する、という感じです。ポインタから構造体への変換のあたりは、.NET Framework におけるポインタ操作の基本ですね。
IntPtr pVm; IntPtr pEnv; JavaVMInitArgs initArgs; JNIResult result; JavaVM vm; JNIEnv env; JNIInvokeInterface jiif; JNINativeInterface jnif; int version; IntPtr pClass; IntPtr pMethodID; jvalue[] arg = { new jvalue() }; int exitCode; initArgs = new JavaVMInitArgs(); initArgs.version = JNI_VERSION_1_6; result = JNI_CreateJavaVM(out pVm, out pEnv, ref initArgs); if (result == JNIResult.JNI_OK) { Console.WriteLine("JNI_CreateJavaVM OK."); } else { Console.WriteLine("JNI_CreateJavaVM NG : {0}.", result); goto fin; } vm = (JavaVM) Marshal.PtrToStructure(pVm, typeof(JavaVM)); env = (JNIEnv) Marshal.PtrToStructure(pEnv, typeof(JNIEnv)); jiif = (JNIInvokeInterface) Marshal.PtrToStructure( vm.functions, typeof(JNIInvokeInterface)); jnif = (JNINativeInterface) Marshal.PtrToStructure( env.functions, typeof(JNINativeInterface)); getVersion = (JNIEnv.GetVersion) Marshal.GetDelegateForFunctionPointer( jnif.GetVersion, typeof(JNIEnv.GetVersion)); version = getVersion.Invoke(pEnv); Console.WriteLine("JVM version : 0x{0:x}", version); findClass = (JNIEnv.FindClass) Marshal.GetDelegateForFunctionPointer( jnif.FindClass, typeof(JNIEnv.FindClass)); pClass = findClass.Invoke(pEnv, "SampleJavaClass"); getStaticMethodID = (JNIEnv.GetStaticMethodID) Marshal.GetDelegateForFunctionPointer( jnif.GetStaticMethodID, typeof(JNIEnv.GetStaticMethodID)); pMethodID = getStaticMethodID.Invoke( pEnv, pClass, "main", "([Ljava/lang/String;)V"); Console.WriteLine("---------------------"); callStaticIntMethodA = (JNIEnv.CallStaticIntMethodA) Marshal.GetDelegateForFunctionPointer( jnif.CallStaticIntMethodA, typeof(JNIEnv.CallStaticIntMethodA)); exitCode = callStaticIntMethodA.Invoke(pEnv, pClass, pMethodID, arg); Console.WriteLine("exit code : {0}", exitCode); destroyJavaVM = (JavaVM.DestroyJavaVM) Marshal.GetDelegateForFunctionPointer( jiif.DestroyJavaVM, typeof(JavaVM.DestroyJavaVM)); destroyJavaVM.Invoke(pVm); fin: Console.WriteLine("------ finish. ------"); Console.ReadLine(); }
といったところで、パスの通ったところに jvm.dll があること、CLASSPATH が通ったところ ( という表現が正しいのか、Java 初心者のわたしはよくわかりません ) に呼び出し対象の .jar を置いて実行できました。
と思ったら、こんなプロジェクト ( csjni ) を見つけてしまいました。ちぇー。