C++ function returning std::string

928 views
Skip to first unread message

CarlH

unread,
Sep 30, 2011, 7:31:03 PM9/30/11
to javacpp
I'm trying to wrap a library function that returns a "std::string" but
the JavaCPP JNI code that is generated tries to assign the return
value to a "const char*". Not sure what I'm doing wrong...

Here's the function declaration from header file:
--------------------------------------------------------------
std::string capitalize( const std::string & str );

The line of Java code:
----------------------------
public static native @Cast("std::string") String
capitalize( @Cast("std::string") String str );

The error when running JavaCPP:
--------------------------------------------
libs/armeabi/jniPystring.cpp:724: error: cannot convert 'std::string'
to 'const char*' in assignment

And the generated JNI:
------------------------------
JNIEXPORT jstring JNICALL Java_com_test_Pystring_capitalize(JNIEnv* e,
jclass c, jstring p0) {
const char* pointer0 = p0 == NULL ? NULL : e-
>GetStringUTFChars(p0, NULL);
jstring r = NULL;
const char* rpointer;
try {
rpointer = pystring::capitalize((std::string)pointer0);
if (rpointer != NULL) {
r = e->NewStringUTF(rpointer);
}
if (p0 != NULL) e->ReleaseStringUTFChars(p0, pointer0);
} catch (...) {
JavaCPP_handleException(e);
}
return r;
}

Samuel Audet

unread,
Sep 30, 2011, 8:49:11 PM9/30/11
to javacpp...@googlegroups.com
Hello,

Yes, for obscure historical reasons, the `std::string` class does not
support implicit conversion to `const char*`, the type required by
Java's `NewStringUTF()`. We need to call `std::string.c_str()` manually,
which is quite a bother. For that reason I added a hack such that it
gets returned that way when declaring `@ByRef String` on the Java side,
i.e. for your function:
public static native @ByRef String capitalize(String str);
should work as expected.

Samuel

On 2011-10-01 08:31, CarlH wrote:
> I'm trying to wrap a library function that returns a "std::string" but
> the JavaCPP JNI code that is generated tries to assign the return
> value to a "const char*". Not sure what I'm doing wrong...
>
> Here's the function declaration from header file:
> --------------------------------------------------------------

> std::string capitalize( const std::string& str );

CarlH

unread,
Oct 1, 2011, 2:10:14 AM10/1/11
to javacpp
Hi Samuel,

Thanks, that worked great!

Now it is generating the code just as you described:
rpointer = pystring::capitalize(pointer0).c_str();

-Carl

Dror Tirosh

unread,
Oct 1, 2011, 5:40:19 AM10/1/11
to javacpp...@googlegroups.com

How about better java attribute names?
Instead of @byref String add new attribute @ctype. 
It will do the best conversion from the java parameter (or return type) and this c type, or choke on error if it can't.

Samuel Audet

unread,
Oct 1, 2011, 5:58:12 AM10/1/11
to javacpp...@googlegroups.com
It's already there, it's named Adapter. Generator provides by default
VectorAdapter for mapping std::vector to Java arrays or JavaCPP Pointer
objects, e.g.:
native void f(@Adapter("VectorAdapter<int>") int[] p);
or
native void f(@Adapter("VectorAdapter<int>") IntPointer p);
And you can come up with your own C++ adapters for other types...

In the case of String, I had it done before coming up with Adapter.
Passing String objects by reference to Java makes no sense because its
memory can't be mapped to native memory, and since this is only a
problem with C++, which has pass-by-reference, unlike C, which does not,
I thought it would be a good use of "@ByRef String" to mean "C++ String" ;)

Samuel

On 2011-10-01 18:40, Dror Tirosh wrote:
> How about better java attribute names?
> Instead of @byref String add new attribute @ctype.
> It will do the best conversion from the java parameter (or return type)
> and this c type, or choke on error if it can't.
>
> On Oct 1, 2011 3:49 AM, "Samuel Audet" <samuel...@gmail.com

Dror Tirosh

unread,
Oct 3, 2011, 7:41:50 AM10/3/11
to javacpp...@googlegroups.com

Adapter works, but doesn't give access to the java type: if i define a String parameter with @Adapter it will pass the char* pointer, not the jstring. Thus i can't write my own adapter.

Samuel Audet

unread,
Oct 3, 2011, 9:20:46 AM10/3/11
to javacpp...@googlegroups.com
On 2011-10-03 20:41, Dror Tirosh wrote:
> Adapter works, but doesn't give access to the java type: if i define a
> String parameter with @Adapter it will pass the char* pointer, not the
> jstring. Thus i can't write my own adapter.

Right, I designed JavaCPP so we did not have to muck around with JNI.. I
could add a @Raw annotation or something for people who really want to
do that I guess, but I really don't see the point. What are you trying
to do here anyway? It's like trying to use a C++ object from assembly
language, a nightmare. Got better things to do...

Samuel

Dror Tirosh

unread,
Oct 3, 2011, 10:08:13 AM10/3/11
to javacpp...@googlegroups.com
1. access in-place arrays: copying arrays is nice, but inefficient for large ones.
2. return multiple strings. so that I can write a c++ function like:
      void getinfo( string& ret_name, string& ret_lastname, string& ret_title )
and wrap into with the java method:
    void GetInfo( StringBuilder ret_name, StringBuilder ret_lastname, StringBuilder ret_title );
 
(in some cases this could be avoided if we had a simple callback from c++ to java...)

-- dror.


Samuel Audet

unread,
Oct 3, 2011, 10:54:36 PM10/3/11
to javacpp...@googlegroups.com
On Mon, Oct 3, 2011 at 11:08 PM, Dror Tirosh <dr...@tothink.com> wrote:
> 1. access in-place arrays: copying arrays is nice, but inefficient for large
> ones.

The only safe, practical, and efficient way of doing that in Java is
with NIO direct buffers. It's also possible to do it efficiently *or*
safely with arrays, but not efficiently *and* safely. But it's /not/
possible to do it efficiently with String, StringBuilder, StringBuffer
or anything else related with strings.

BTW, copying something of 1 kB isn't "large", so do not worry about
copying stuff that is so small. It all fits in L1 cache

> 2. return multiple strings. so that I can write a c++ function like:
>       void getinfo( string& ret_name, string& ret_lastname, string&
> ret_title )
> and wrap into with the java method:
>     void GetInfo( StringBuilder ret_name, StringBuilder ret_lastname,
> StringBuilder ret_title );

It's possible to do have
void getInto(char *ret_name, char *ret_lastname, char *ret_title)
that maps directly to
void getInfo(ByteBuffer ret_name, ByteBuffer ret_lastname,
ByteBuffer ret_title)

If you really really want to use std::string, then it's possible to
make an Adapter for that yes, which would look like this:
void getInfo(@Adapter("StringAdapter") BytePointer ret_name,
@Adapter("StringAdapter") BytePointer ret_lastname,
@Adapter("StringAdapter") BytePointer ret_title)

> (in some cases this could be avoided if we had a simple callback from c++ to
> java...)

JavaCPP does support callback functions, but it's inefficient and
hackish to call into the JVM from native code, so I would not
recommend it

Samuel

Reply all
Reply to author
Forward
0 new messages