M
mrts
First, a bit of high-level context:
I'm using JNI in a C++ project. Each JNI resource
should be accessed via a reference to the resource
object inside the Java virtual machine (JVM)
environment. That reference needs to be released
with when the resource is no longer needed.
E.g. to create a string, one would call
jstring str_ref = env_->NewStringUTF("foo");
where env_ is the pointer to the JVM environment,
and to release the resources of the string,
_env->DeleteLocalRef(str_ref);
This is a classic case of RAII. A smart "handle"
that would release the reference in it's
destructor would be a perfect, exception-safe
casing for the naked JVM references.
So far, so good.
In the spirit of proper encapsulation, all of the
JNI machinery is designed to be hidden behind a
high-level wrapper class, JNIWrapper. This is a
singleton class that owns all the JVM-related
resources, including the JVM environment pointer.
New resources should be created according to the
source/sink idiom [1], hiding the private
environment pointer:
JVMRef<jstring> JNIWrapper::newString(const char* str)
{
// env_ is a private member of the JNIWrapper
// instance
JVMRef<jstring> ret(env_, env_->NewStringUTF(str));
return ret;
}
where JVMRef should be an auto_ptr like
transferable-ownership class that needs a custom
deleter that has access to the environment. (Note
that I cannot use C++0X's std::unique_ptr.)
Here's a simple, auto_ptr-like first stab at
JVMRef:
template <typename T>
class JVMRef {
public:
JVMRef(JNIEnv *env, T ref) :
env_(env), ref_(ref) { }
JVMRef(JVMRef& other) :
env_(other.get_env()), ref_(other.release()) { }
JVMRef& operator=(JVMRef &other)
{ reset(other.release()); return *this; }
~JVMRef() { reset(NULL); }
T get()
{ return ref_; }
JNIEnv *get_env()
{ return env_; }
T release()
{ T ret = ref_; ref_ = NULL; return ret; }
void reset(T other = NULL) {
if (ref_ != other) {
env_->DeleteLocalRef(ref_);
ref_ = other;
}
}
private:
JNIEnv *env_;
T ref_;
};
The "source" function is given in
JNIWrapper::newString() above. Attempts to
use it in a "sink" function to construct a new
JVMRef to a Java string as follows:
// jni is the instance of JNIWrapper
JVMRef<jstring> jfoo(jni.newString("foo"));
fail as follows with g++ 4.4.1:
---
error: no matching function for call to
‘JNI::JVMRef<_jstring*>::JVMRef(JNI::JVMRef<_jstring*>)’
note: candidates are:
JNI::JVMRef<T>::JVMRef(JNI::JVMRef<T>&)
[with T = _jstring*]
note:
JNI::JVMRef<T>::JVMRef(JNIEnv*, T)
[with T = _jstring*]
---
That's probably because the temporary that's created
when the "source" returns can't be used by reference.
How should I amend JVMRef to make it appease
the compiler? (Btw, I'm aware of Boost's shared_ptr that
supports deleters, but it isn't a neat fit.)
[1] http://www.gotw.ca/publications/using_auto_ptr_effectively.htm
I'm using JNI in a C++ project. Each JNI resource
should be accessed via a reference to the resource
object inside the Java virtual machine (JVM)
environment. That reference needs to be released
with when the resource is no longer needed.
E.g. to create a string, one would call
jstring str_ref = env_->NewStringUTF("foo");
where env_ is the pointer to the JVM environment,
and to release the resources of the string,
_env->DeleteLocalRef(str_ref);
This is a classic case of RAII. A smart "handle"
that would release the reference in it's
destructor would be a perfect, exception-safe
casing for the naked JVM references.
So far, so good.
In the spirit of proper encapsulation, all of the
JNI machinery is designed to be hidden behind a
high-level wrapper class, JNIWrapper. This is a
singleton class that owns all the JVM-related
resources, including the JVM environment pointer.
New resources should be created according to the
source/sink idiom [1], hiding the private
environment pointer:
JVMRef<jstring> JNIWrapper::newString(const char* str)
{
// env_ is a private member of the JNIWrapper
// instance
JVMRef<jstring> ret(env_, env_->NewStringUTF(str));
return ret;
}
where JVMRef should be an auto_ptr like
transferable-ownership class that needs a custom
deleter that has access to the environment. (Note
that I cannot use C++0X's std::unique_ptr.)
Here's a simple, auto_ptr-like first stab at
JVMRef:
template <typename T>
class JVMRef {
public:
JVMRef(JNIEnv *env, T ref) :
env_(env), ref_(ref) { }
JVMRef(JVMRef& other) :
env_(other.get_env()), ref_(other.release()) { }
JVMRef& operator=(JVMRef &other)
{ reset(other.release()); return *this; }
~JVMRef() { reset(NULL); }
T get()
{ return ref_; }
JNIEnv *get_env()
{ return env_; }
T release()
{ T ret = ref_; ref_ = NULL; return ret; }
void reset(T other = NULL) {
if (ref_ != other) {
env_->DeleteLocalRef(ref_);
ref_ = other;
}
}
private:
JNIEnv *env_;
T ref_;
};
The "source" function is given in
JNIWrapper::newString() above. Attempts to
use it in a "sink" function to construct a new
JVMRef to a Java string as follows:
// jni is the instance of JNIWrapper
JVMRef<jstring> jfoo(jni.newString("foo"));
fail as follows with g++ 4.4.1:
---
error: no matching function for call to
‘JNI::JVMRef<_jstring*>::JVMRef(JNI::JVMRef<_jstring*>)’
note: candidates are:
JNI::JVMRef<T>::JVMRef(JNI::JVMRef<T>&)
[with T = _jstring*]
note:
JNI::JVMRef<T>::JVMRef(JNIEnv*, T)
[with T = _jstring*]
---
That's probably because the temporary that's created
when the "source" returns can't be used by reference.
How should I amend JVMRef to make it appease
the compiler? (Btw, I'm aware of Boost's shared_ptr that
supports deleters, but it isn't a neat fit.)
[1] http://www.gotw.ca/publications/using_auto_ptr_effectively.htm