Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Custom transferable-ownership classes

Reply
Thread Tools

Custom transferable-ownership classes

 
 
mrts
Guest
Posts: n/a
 
      08-16-2010
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<_jstri ng*>)’

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/usin...ffectively.htm
 
Reply With Quote
 
 
 
 
Vladimir Jovic
Guest
Posts: n/a
 
      08-17-2010
mrts wrote:
>
> 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<_jstri ng*>)’
>
> note: candidates are:
> JNI::JVMRef<T>::JVMRef(JNI::JVMRef<T>&)
> [with T = _jstring*]
>
> note:
> JNI::JVMRef<T>::JVMRef(JNIEnv*, T)
> [with T = _jstring*]


Read what the compiler is telling you. You do not have constructor with
next signature :

JVMRef(SNI::JVMRef<_jstring*>);

your constructors are :
JVMRef(JNIEnv *env, T ref)
JVMRef(JVMRef& other) <<< copy constructor

What is the return of jni.newString("foo") ?
 
Reply With Quote
 
 
 
 
mrts
Guest
Posts: n/a
 
      08-17-2010
On Aug 17, 11:05*am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> mrts wrote:
>
> > 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<_jstri ng*>)’

>
> > note: candidates are:
> > JNI::JVMRef<T>::JVMRef(JNI::JVMRef<T>&)
> > [with T = _jstring*]

>
> > note:
> > JNI::JVMRef<T>::JVMRef(JNIEnv*, T)
> > [with T = _jstring*]

>
> Read what the compiler is telling you. You do not have constructor with
> next signature :
>
> JVMRef(SNI::JVMRef<_jstring*>);
>
> your constructors are :
> JVMRef(JNIEnv *env, T ref)
> JVMRef(JVMRef& other) * * * <<< copy constructor
>
> What is the return of jni.newString("foo") ?


A temporary, as shown above:

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;
}

---

I do understand that the temporary is the culprit here
and that there is no constructor that accepts JVMRef
by value.

I've examined the definition of auto_ptr meanwhile
and found the trickery of auto_ptr_ref that makes
auto_ptr work correctly in the source/sink scenario.

So, sorry for the fuss and for not thinking with
the required rigour . I've settled for the following
to provide the functionality needed:

---

template <typename T>
struct jvmref_ref
{
explicit jvmref_ref(JNIEnv *e, T p) : _env(e), _ptr(p) { }

JNIEnv *_env;
T _ptr;
};

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(jvmref_ref<T> ref) : _env(ref._env), _ref(ref._ptr) { }

operator jvmref_ref<T>()
{ return jvmref_ref<T>(this->get_env(), this->release()); }

~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 = 0)
{
if (_ref != other) {
_env->DeleteLocalRef(_ref);
_ref = other;
}
}

private:
JVMRef& operator=(const JVMRef&);
JNIEnv *_env;
T _ref;
};

---

That solves the general problem of how should one handle factory
functions that would otherwise use auto_ptr, but need a custom deleter
and can not use std::unique_ptr or boost::shared_ptr.

(Any insight on the wrongs of the above is of course much welcome.)
 
Reply With Quote
 
Vladimir Jovic
Guest
Posts: n/a
 
      08-17-2010
mrts wrote:
>
> 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;
> }
>


>
> 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<_jstri ng*>)’
>
> note: candidates are:
> JNI::JVMRef<T>::JVMRef(JNI::JVMRef<T>&)
> [with T = _jstring*]
>
> note:
> JNI::JVMRef<T>::JVMRef(JNIEnv*, T)
> [with T = _jstring*]
>


Sorry to be PITA again, but the compiler complains about
JVMRef<_jstring*> type, and your bits and pieces of the example are
showing you used JVMRef<jstring>.

If you copy and paste the fully compilable example (including main), you
will get much better response :
http://www.parashift.com/c++-faq-lit...t.html#faq-5.8


> That's probably because the temporary that's created
> when the "source" returns can't be used by reference.


Is that legal?
 
Reply With Quote
 
mrts
Guest
Posts: n/a
 
      08-17-2010
On Aug 17, 12:54*pm, Vladimir Jovic <vladasp...@gmail.com> wrote:
> mrts wrote:
>
> > 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;
> > }

>
> > 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<_jstri ng*>)’

>
> > note: candidates are:
> > JNI::JVMRef<T>::JVMRef(JNI::JVMRef<T>&)
> > [with T = _jstring*]

>
> > note:
> > JNI::JVMRef<T>::JVMRef(JNIEnv*, T)
> > [with T = _jstring*]

>
> Sorry to be PITA again, but the compiler complains about
> JVMRef<_jstring*> type, and your bits and pieces of the example are
> showing you used JVMRef<jstring>.
>
> If you copy and paste the fully compilable example (including main), you
> will get much better response :http://www.parashift.com/c++-faq-lit...t.html#faq-5.8


Yes, sorry for that... as for jstring, it's defined as follows in
Sun's jni.h:

class _jstring : public _jobject {};
typedef _jstring *jstring;

> > That's probably because the temporary that's created
> > when the "source" returns can't be used by reference.

>
> Is that legal?


No, and that was exactly the problem. To overcome this, the
struct jvmref_ref trick is needed (the trick lies in defining
`JVMRef(jvmref_ref<T> ref)` and `operator jvmref_ref<T>()`
and it is a shameless copy from std::auto_ptr).
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Classes within classes David ASP .Net 2 07-22-2005 07:13 PM
What is the difference between nested classes and inner classes ? Razvan Java 5 07-27-2004 07:59 PM
Modifiers applied to attributes, local variables, member functions, classes and inncer classes ! Razvan Java 11 07-17-2004 08:57 PM
Can I using reflection to get all child classes or classes undera package dynamically? Carfield Yim Java 1 05-31-2004 05:33 PM
How to access inner classes variables & methods from outer classes lonelyplanet999 Java 1 11-13-2003 01:54 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57