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 ) を見つけてしまいました。ちぇー。