C++ããC++ãããJNIãå¼ãã§ã¿ã
ãJNIï¼Java Native Interfaceï¼ã¯Javaã¨Cã®é£æºæè¡ã§ããJavaã¢ããªã±ã¼ã·ã§ã³ã®ä¸é¨ãã¸ãã¯ãCã§æ¸ããããããã«ãã®ä¸ããJavaã®ã¡ã½ãããå¼ã³åºãããããäºã§ãã¢ããªã±ã¼ã·ã§ã³ã®å®è¡ç°å¢ãå¶éããã代ããã«é«éåããããã¯éJavaãã©ãããã©ã¼ã ã®ã³ã¼ãã¨ã®å ±éåãè¡ãªãããã¨ãã寸æ³ã§ãã
JavaããC++ã®é¢æ°ãå¼ã³åºãå ´å
ãJNIãç¨ãã¦JavaããC++ã®é¢æ°ãå¼ã¶å ´åããããªæãã«ãªãã¾ãï¼
// Java package com.cflat; public class Hoge { static { System.loadLibrary("hoge"); } public static native boolean printNative(String s, int x); public static boolean printJava(String s, int x) { System.out.println(s + " " + x); return true; } }
// C++ extern "C" { JNIEXPORT jboolean JNICALL Java_com_cflat_Hoge_print(JNIEnv *env, jstring s, jint x); }
ãCå½åè¦åã§ãJava\_<i>packagename</i>\_<i>classname</i>\_<i>methodname</i>()
ãªãé¢æ°ãä½ã£ã¦ããã°OKâ¦â¦C++çã«ã¯å«ãªæãã®ååã§ãããååãã³ã°ãªã³ã°ãå®è£
ä¾åã«ãªã£ã¦ããC++ã§ã¯ãä»ã®è¨èªããé¢æ°ãå¼ãã§è²°ããã¨æãã¨extern "C"
ãå¿
é ã§ãã®ã§ãã¾ãä»æ¹ããã¾ãããããã¦ããå°ãçãååãã ã¨è¨ãã®ã§ããã°JNIã§ã¯ãªãJNAï¼Java Native Accessï¼ã使ãã¨ããæ¹æ³ãæ®ããã¦ãã¾ãã
C++ããJavaã®ã¡ã½ãããå¼ã³åºãå ´åã
ã次ã«ãä»åº¦ã¯C++ããJavaãå¼ã³åºãã¦ã¿ã¾ãã
ãã¾ããC++å´ã§ã¯JavaVM
ã®ã¤ã³ã¹ã¿ã³ã¹ã«å¯¾ãã¦GetEnv()
ãå¼ã³åºãäºã§JNIEnv*
ãåå¾ããªããã°ãªããªãã®ã§ããããã®JNIEnv*
ã®å¤ã¯ã¹ã¬ãããã¨ã«å¤ãã£ã¦ãã¾ãã¾ãã
ãããããC++å´ã§ä½ãããã¹ã¬ããã«ã¯ï¼å½ç¶ãªããï¼æåã¯JNIEnv*
ãä½ããã¦ãã¾ããã®ã§ãã¾ãã¯AttachCurrentThread()
ããªããã°ãªãã¾ããããã¡ãããã¹ã¬ããç ´æ£æã«ã¯DetachCurrentThread()
ãå¿
è¦ã§ãã
ããã ããã®è¾ºã¯ä»åããã話ã¨ã¯å°ãããã¦æ¥ã¾ãã®ã§çç¥ãã¦ãæ¢ã«JNIEnv*
ãå¾ãã¨ããããå§ãããã¨æãã¾ãã
ãããã¦â¦â¦å®éã«ãã£ã¦ã¿ãã¨ã次ã®ããã«ã¤ã±ã¦ãªãé¨åã°ããã®ä»£ç©ã«ãªã£ã¦ãã¾ãã¾ãï¼
// C++ bool printJava(JNIEnv *env, const std::string &s, int32_t x) { jclass klass = env->FindClass("com/cflat/Hoge"); jmethodID method = env->GetMethodID(klass, "printJava", "(Ljava/jang/String;I)Z"); // âã¤ã±ã¦ãªããã®1 jstring jstr = env->NewStringUTF(s.c_str()); jboolean result = env->CallStaticBooleanMethod(klass, method, jstr, x); // âã¤ã±ã¦ãªããã®2 env->DeleteLocalRef(jstr); // âã¤ã±ã¦ãªããã®3 env->DeleteLocalRef(klass); // ââ return (result != JNI_FALSE); }
ãJNIã¯C++ã§ã使ããã¨ã¯è¨ã£ã¦ããCç¨ã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ã®æä½éã®é¨åã#if defined(__cplusplus)
ã#endif
ã§å²ã£ãã ãã®ã©ã¤ãã©ãªã§ããããä»æ¹ãªãäºã§ã¯ããã¾ãã
ããããã£ã¨C++ãããããã®ã§ããã°ãããã¦ãããªå½¢ã«ãããã¨ããã§ãããï¼
// C++ bool printJava(jni::vm &vm, const std::string &s, int32_t x) // jni::vmã¯JavaVMã®ã©ããã¼ï¼èªåçã«é©åãªJNIEnv*ãåå¾ããï¼ { jni::class_info klass = vm.get_class("com/cflat/Hoge"); // jni::class_infoã¯jclassã®ã©ããã¼ auto method = klass.get_static_method<bool(std::string, int32_t)>("printJava"); // ã·ã°ããã£ã¼ã¯åæå®ã§ï¼ return method(s, x); // å¼ã³åºãããget_static_method()ã«æ¸¡ããåã®éãã«operator()ã§ï¼ // jclassããjstringããã¯RAIIã§åæã«ç ´æ£ãããã®ã§ãããããDeleteLocalRef()ããªãã¦OKï¼ }
ãJNIã®åã®C++ãããã©ããã¼ã§ããjni::vm
ãjni::class_info
ã¯ãããç¨åº¦C++ã«æ
£ããã°èª°ã§ãä½ããã¯ãã§ããï¼ã³ãã¼ã³ã³ã¹ãã©ã¯ã¿ã¨ä»£å
¥æ¼ç®åã§NewLocalRef()
ããã¹ãã©ã¯ã¿ã¨ä»£å
¥æ¼ç®åã§DeleteLocalRef()
ãå¿ããªãããã«ï¼ï¼ãã·ã°ããã£ã¼ãåæå®ã§æ¸ã¾ãããã«ä½ãã«ã¯ãããªãC++ã®ãã³ãã¬ã¼ãã«æ
£ãã¦ããªãã¨é£ããã®ã§ã¯ãªãã§ããããï¼
ããã®ã¯è©¦ããã¡ãã£ã¨ä½ã£ã¦ã¿ã¾ãããã
JNIã©ããã¼ã®æ§æ
ãä¸è¨ã®ã³ã¼ããè¦ã¦æ°ä»ããæ¹ãããã¨æãã¾ãããã·ã°ããã£ã¼ã®åæå®ã¯JNIã®åã§ããjstring
ãjint
ã§ã¯ãªããC++ã®åã§ããstd::string
ãint32_t
ã使ã£ã¦ãã¾ããä½æ
ãªããã¡ã½ããå¼ã³åºãå¦çã®å
é¨ã§èªåå¤æãã¦ãããæ¹ãæ±ããããã®ã§ã
å ´åã«ãã£ã¦ã¯ãåãjstring
ã®ã¤ã³ã¹ã¿ã³ã¹ãè¤æ°ã®ã¡ã½ããå¼ã³åºãã§å
±æããããã¨ããå ´åãããããããã¾ãããï¼å½ç¶ããã®æ¹ãä½è¨ãªãªã¼ãã¼ããããä¸è¦ã«ãªãã¾ãï¼ãä»åã¯æ°ã«ããªããã®ã¨ãã¾ããå¿
è¦ã§ããã°jstring
ã®ã©ããã¼ââjstring
ã®å¤æã¯ã©ã¹ã追å ãã¦ããã°ããã ãã§ãããã
ãããããéã¿ãã¨ãJNIã®C++ãããã©ããã¼ã®å®è£ ã¯ã次ã®ãããªé¨åããæãããã«ã§ããã§ãããï¼
- C++åââJNIåå¤æé¨
- JNIåââã·ã°ããã£ã¼æååå¤æé¨
- JNIåââCallXXXMethodå¤æé¨
ããªããjstring
ï¼ãããã¯ä»ã®ãªãã¡ã¬ã³ã¹åï¼ãRAIIã§ããæãã«æ§ç¯ï¼ç ´æ£ãã¦ãããé¨åã1.ã®ä¸ã«å«ã¾ãããã®ã¨ãã¾ãã
C++ââJNIå
ãã¾ãã¯ãC++åãæå®ããæã«å¯¾å¿ããJNIåãæ±ããããã®ã¡ã¿é¢æ°ãå¿ è¦ã«ãªãã¾ãã ããã®é¨åãã©ãè¨è¨ãããã¯æ©ã¾ããã®ã§ããã試ãã«ã次ã®ããã«ãã¦ã¿ã¾ãããï¼
- 1/2/4/8ãã¤ãã®ç¬¦å·ä»ãæ´æ°åã¯ããããã
jbyte
/jshort
/jint
/jlong
ã«å¤æãã((æ¬å½ã¯ãint8/16/32/64_t
ã®ã¿ãã¨ãããã£ãã¨ããã§ãããint
ã§æå®ã§ããªãã®ããªããå«ãªã®ã§â¦â¦)) - 4/8ãã¤ãã®æµ®åå°æ°åã¯ããããã
jfloat
/jdouble
ã«å¤æãã char16_t
ã¨jchar
ã¯jchar
ã«å¤æãã((sizeof(wchar_t) == 2
ã®å ´åãwchar_t
ãjchar
ã«ãã¦ãããã))bool
ã¨jboolean
ã¯jboolean
ã«å¤æããtemplate <typename TNative> struct object_converter
ãã¦ã¼ã¶ã¼ãæ示çã«ç¹æ®åããã¯ã©ã¹ã¯jobject
ã¾ãã¯äºææ§ãæã¤åã«å¤æãã
ã4.ã¾ã§ã¯ããªããã£ãã5.ã¯ã¯ã©ã¹åã§ããã
ãâ¦â¦ã§ããã®é¨åã解説ãããã¨æã£ãã®ã§ããã詳細ã«è§£èª¬ããã¨å¤§å¤ãªäºã«ãªãã®ã§ãããããã¨ä¸æãç¹æ®åæ¸ã¿ã®æ¬¡ã®ã¯ã©ã¹ããããã®ã¨ãã¾ãï¼ã³ãã¼ãã ã¼ãããã¹ãã©ã¯ã¿ã¯çç¥ãã¦ã¾ãï¼ã
template <typename TNative> struct var { public: typedef TNative native_type; typedef 対å¿ããJNIå jni_type; var(JNIEnv *, native_type); var(JNIEnv *, jni_type); operator native_type() const; operator jni_type() const; private: jni_type m_type; };
ããã®ã¯ã©ã¹ã®ç¹æ®ã±ã¼ã¹ã¨ãã¦ãã¤ã³ã¹ã¿ã³ã¹åã®ã§ããªã次ã®ãã®ãç¨æãã¾ãï¼
template <> struct var<void> { typedef void native_type; typedef void jni_type; var() = delete; }; template <class TNativeRet, class ... TNativeParams> struct var<TNativeRet(TNativeParams...)> { typedef TNativeRet native_type(TNativeParams...); typedef typename var<TNativeRet>::jni_type jni_type(typename var<TNativeParams>::jni_type...); var() = delete; };
ãâ¦â¦ããã§ãé¢æ°ã¿ã¤ãã®ã·ã°ããã£ã¼ã«å¯¾ãã¦ã対å¿ããJNIã®é¢æ°ã¿ã¤ããæ±ããããããã«ãªãã¾ããã
JNIåââã·ã°ããã£ã¼æåå
ãJNIã®ã·ã°ããã£ã¼æååã«ã¯ã大ããåãã¦4ã¤ã®ãã¿ã¼ã³ãããã¾ãã
- è±å1æåï¼ããªããã£ãåï¼
- "L"ï¼ã¯ã©ã¹åï¼";"ï¼éããªããã£ãåï¼
- "["ï¼ä»ã®åï¼é ååï¼
- "("ï¼å¼æ°åã·ã°ããã£ã¼ãªã¹ãï¼")"ï¼æ»ãå¤åï¼é¢æ°ã·ã°ããã£ã¼ï¼
ãããããã§ããã°ã³ã³ãã¤ã«æã«jint
ã¨æ¸¡ãã°"I"ãjstring
ã¨æ¸¡ãã°"Ljava/lang/String;"ãvoid(jstring, jint)
ã¨æ¸¡ãã°"(Ljava/lang/String;I)V"ã®ããã«æååãæ§ç¯ã§ããã°è¯ãã®ã§ãããåé¡ã¯2.ã®ã¯ã©ã¹åãã¿ã¼ã³ã
ãã¨ããã®ããstd::string
ã¯åºæ¬çã«constexpr
ã«å¯¾å¿ãã¦ãã¾ããããconst char*
ã§ã¯æååçµåãå°é£ã ããã§ãããã³ãã¬ã¼ãã使ãã¨ãã¦ãããã³ãã¬ã¼ãã§æååãªãã©ã«ãä¸ããããªãã®ã§ãã³ã³ãã¤ã«æã«æååæ§ç¯ãããäºãã§ãã¾ããï¼ãæååå¤æ°ã®ã¢ãã¬ã¹ããæå®ããäºã ãã¯å¯è½ã§ãããæ示å
ãã³ã³ãã¤ã«æã«åç
§ã§ããªãã®ã§æå³ãªãï¼ã
ããããªæã☆C++11テクニック☆ 配列を配列で初期化する方法+α - 株式会社CFlatの明後日スタイルのブログã§ç´¹ä»ããæ¹æ³ã使ã£ã¦ãconstexpr
ãªæååçµåãè¡ãªã£ã¦ããã°ããã§ãããã
ãããã ãããã£ã¦ããã°ãå¾ã¯é¢åãªã ãã§å¤§ããäºãªãã®ã§å²æãã¾ãã
ãã¨ããããsignature<T>::get()
ãããã§åãã¦ããããã«ããã°ããã§ãããã
JNIåââCallXXXMethod
ãããã¾ã§æ¥ãã°ãå¾ã¯å®å ¨ã«ä½æ¥ã§ãããã¾ãã次ã®ãããªã¯ã©ã¹ãä½ãâ¦â¦
template <typename TJniRet> struct method_caller_info { static_assert(std::is_convertible<TJniRet, jobject>::value, "TJniRet is not a primitive type, void, nor jobject"); static auto static_method_caller() -> decltype(&JNIEnv::CallStaticObjectMethod) { return &JNIEnv::CallStaticObjectMethod; } static auto instance_method_caller() -> decltype(&JNIEnv::CallObjectMethod) { return &JNIEnv::CallObjectMethod; } static auto nonvirtual_method_caller() -> decltype(&JNIEnv::CallNonvirtualObjectMethod) { return &JNIEnv::CallNonvirtualObjectMethod; } }; template <typename TJniRet> struct method_caller : method_caller_info<TJniRet> { typedef TJniRet result_type; template <typename ... TJniParams> static result_type call_static(const class_info &klass, jmethodID method_id, TJniParams ... params) { return (result_type)(klass.jni_env()->*method_caller_info<TJniRet>::static_method_caller())(klass.jni_value(), method_id, params ...); } template <typename TJObject, typename ... TJniParams> static result_type call_instance(const object<TJObject> &obj, jmethodID method_id, TJniParams ... params) { return (result_type)(obj.jni_env()->*method_caller_info<TJniRet>::instance_method_caller())(obj.jni_value(), method_id, params ...); } template <typename TJObject, typename ... TJniParams> static result_type call_nonvirtual(const object<TJObject> &obj, const class_info &klass, jmethodID method_id, TJniParams ... params) { if (obj.jni_env() != klass.jni_env()) throw env_mismatch(); return (result_type)(obj.jni_env()->*method_caller_info<TJniRet>::nonvirtual_method_caller_type())(obj.jni_value(), klass.jni_value(), method_id, params ...); } };
ããªããã£ãåç¨ã«ãC++ããããªãã¦å«ã§ãããããããæã¯Cããªããã»ããµãã¯ãã«ãä»»ãã
#define DEFINE_PRIMITIVE_METHOD_CALLER(jni_primitive_type, methodName) \ template <> \ struct method_caller_info<jni_primitive_type> \ { \ static auto static_method_caller() -> decltype(&JNIEnv::CallStatic##methodName##Method) { return &JNIEnv::CallStatic##methodName##Method; } \ static auto instance_method_caller() -> decltype(&JNIEnv::Call##methodName##Method) { return &JNIEnv::Call##methodName##Method; } \ static auto nonvirtual_method_caller() -> decltype(&JNIEnv::CallNonvirtual##methodName##Method) { return &JNIEnv::CallNonvirtual##methodName##Method; } \ }; DEFINE_PRIMITIVE_METHOD_CALLER(void, Void); DEFINE_PRIMITIVE_METHOD_CALLER(jboolean, Boolean); DEFINE_PRIMITIVE_METHOD_CALLER(jbyte, Byte); DEFINE_PRIMITIVE_METHOD_CALLER(jchar, Char); DEFINE_PRIMITIVE_METHOD_CALLER(jshort, Short); DEFINE_PRIMITIVE_METHOD_CALLER(jint, Int); DEFINE_PRIMITIVE_METHOD_CALLER(jlong, Long); DEFINE_PRIMITIVE_METHOD_CALLER(jfloat, Float); DEFINE_PRIMITIVE_METHOD_CALLER(jdouble, Double);
ãããã§ã¾ããæ»ãå¤ã®åãæå®ãã¦é©åãªCallXXXMethod()
ãåå¾ãã¦ãããã¯ã©ã¹method_caller
ãä½ãã¾ãããå¾ã¯ãå®éã«å¼ã³åºãå¦çãä½ãã°ããã§ãããã
ãä¾ã«ãã£ã¦ããããã¨çç¥ãã¦ãstatic
ã¡ã½ããã¯ã©ã¹ã ãå®è£
ãã¦ã¿ã¾ãï¼
class method_info { protected: typedef jmethodID (JNIEnv::*get_method_func_type)(jclass klass, const char* name, const char* signature); method_info(const class_info &klass, get_method_func_type func, const char *name, const char *signature); class_info m_class; jmethodID m_method; }; template <class TRet> struct static_caller { template <class ... TMethodParams> static typename marshal::var<TRet>::native_type call(const class_info &klass, jmethodID method, TMethodParams ... params) { typedef typename marshal::method_caller<typename marshal::var<TRet>::jni_type> method_caller; auto retval = method_caller::call_static(klass, method, params ...); return (typename marshal::var<TRet>::native_type)marshal::var<TRet>(klass.jni_env(), retval); } }; template <> struct static_caller<void> { template <class ... TMethodParams> static void call(const class_info &klass, jmethodID method, TMethodParams ... params) { typedef typename marshal::method_caller<void> method_caller; method_caller::call_static(klass, method, params ...); } }; template <class TRet, class ... TParams> class static_method_info<TRet(TParams...)> : private method_info { public : static_method_info(const class_info &klass, const char *name) : method_info(klass, &JNIEnv::GetStaticMethodID, name, marshal::signature<TRet(TParams...)>()) {} typename marshal::var<TRet>::native_type operator()(const TParams & ... params) { if (!m_method) throw std::runtime_error("jni: Invalid method is called."); return static_caller<TRet>::call(m_class, m_method, (typename marshal::var<TParams>::jni_type)marshal::var<TParams>(m_class.jni_env(), params) ...); } };
ãéä¸ã®static_caller
ã¯ã©ã¹ã¯ãæ»ãå¤ãvoid
ã¨ãã以å¤ã§ãæ»ãå¤ã®åå¤æãããå¿
è¦ããããã©ãããå¤ãããããé¢æ°ãã³ãã¬ã¼ãã¯é¨åç¹æ®åãã§ããªããããã¯ã©ã¹ãä½ã£ã¦ãã¾ãã
å®éã®ã³ã¼ã
ãå®éã«ãå¼ç¤¾ã¢ããªãFuseãã®Androidçã§ãä¸è¨ã®ä»çµã¿ã使ã£ã¦ã¿ã¾ããã
ãFuseã¯ãªãã¹ãå
±éã®ã½ã¼ã¹ã³ã¼ããç¨ãã¦iPhone/Android両対å¿ã§ãããããCocos2d-xã«ã¦éçºããã¦ãã¾ãã®ã§ãåºæ¬çãªé¨åã¯C++ã§éçºãããã©ãããã©ã¼ã ä¾åãªé¨åã®ã¿ãObjective-C++ããããã¯Javaï¼JNIã§éçºãã¦ãã¾ã((ä¸å¿ãCocos2d-xã®Androidç¨ããã¸ã§ã¯ãã«ã¯ãJNIEnv*
ã®åå¾é¨åã ããå¾®å¦ã«ä½¿ãæããããããªãã«ãã¼ã¯ã©ã¹ãããã®ã§ãã))ã
ãFuseã§ã¯ãä¾ãã°Google Analyticsã®ã¤ãã³ãéä¿¡çãC++ã³ã¼ãããè¡ãªãããã«ãJavaã§æ¬¡ã®ãããªã¯ã©ã¹ãä½ã£ã¦ãã¾ãï¼
package com.cflat; import com.google.analytics.tracking.android.*; import android.app.Activity; public class GoogleAnalyticsTracker { static Activity m_activity = null ; public static void sendAppView(String screenName) { if (m_activity == null) return ; EasyTracker tracker = EasyTracker.getInstance(m_activity) ; tracker.set(Fields.SCREEN_NAME, screenName); tracker.send(MapBuilder.createAppView().build()); } public static void sendEvent(String category, String action, String label, int value) { if (m_activity == null) return ; EasyTracker tracker = EasyTracker.getInstance(m_activity) ; tracker.send(MapBuilder.createEvent(category, action, label, Long.valueOf(value)).build()) ; } public static void initialize(Activity activity) { m_activity = activity ; EasyTracker.getInstance(m_activity).activityStart(m_activity); } public static void terminate() { EasyTracker.getInstance(m_activity).activityStop(m_activity); } }
ãããããC++å´ããã¯æ¬¡ã®ãããªå½¢ã§å¼ã³åºãã¦ãã¾ãï¼
using namespace cflat::jni; bool Tracker::sendAppViewImpl(const char *screenName) { auto method = vm::instance().get_class(CLASS_NAME).get_static_method<void(std::string)>("sendAppView") ; if (!method) return false ; method(screenName) ; return true ; } bool Tracker::sendEventImpl(const char *category, const char *action, const char *label, int value) { auto method = vm::instance().get_class(CLASS_NAME).get_static_method<void(std::string, std::string, std::string, int)>("sendEvent") ; if (!method) return false ; method(category, action, label, value) ; return true ; }
ãJNIããã®ã¾ã¾ä½¿ãã®ã¨æ¯ã¹ãããªããããããããªã£ã¦ããäºãããããããã ãããã¨æãã¾ãã