String to const char* in extern "C" c++, not working forme

537 views
Skip to first unread message

Jerven Bolleman

unread,
Jun 24, 2022, 11:16:45 AM6/24/22
to Java Native Access
Hi Everyone,

First of all thanks for this really nice library it is really helpful.

I am trying to access a C++ library called odgi https://github.com/pangenome/odgi from java using JNA.  Specifically this branch https://github.com/jervenBolleman/odgi/tree/jervenCrazyC. The jna using code is at https://github.com/jervenBolleman/odgi4j

So there is this method called odgi_load_graph(const char* filen) in the file odgi/src/odgi-api.(h|cpp) that I am trying to access.  And the code gets called. However no matter what I have thought of I can't get a conversion from a String to the const char *filen to work :(

The failure is visible with a mvn test on the java code. The library is included for linux-x86-64 in the jar.

Would anyone with more knowledge be able to help or offer some ideas?

Regards,
Jreven






Matthias Bläsing

unread,
Jun 24, 2022, 12:54:51 PM6/24/22
to jna-...@googlegroups.com
Hi,

Am Freitag, dem 24.06.2022 um 08:16 -0700 schrieb Jerven Bolleman:
>
> I am trying to access a C++ library called odgi
> https://github.com/pangenome/odgi from java using JNA.  Specifically
> this branch https://github.com/jervenBolleman/odgi/tree/jervenCrazyC.
> The jna using code is at https://github.com/jervenBolleman/odgi4j
>
> So there is this method called odgi_load_graph(const char* filen) in
> the file odgi/src/odgi-api.(h|cpp) that I am trying to access.  And
> the code gets called. However no matter what I have thought of I
> can't get a conversion from a String to the const char *filen to work
> :(
>
> The failure is visible with a mvn test on the java code. The library
> is included for linux-x86-64 in the jar.
>
> Would anyone with more knowledge be able to help or offer some ideas?

not sure I can help, but the subject has 'in extern "C"' and looking at
the header this is not true:

https://github.com/pangenome/odgi/blob/master/src/odgi-api.h#L114-L118

What is see is an empty "Language agnostic C interface". See the
definition of odgi_version this is something I would expect on the API.
It is C++ specific, as it returns a std::string.

To me it seems noone bothered to create a plain C interface and the
python interface links against the C++ interface.

The API implementation looks small enough that it might be realistic to
turn it into a real C one, that can be called without C++ name
mangeling and types.

Greetings

Matthias

Jerven Bolleman

unread,
Jun 26, 2022, 12:44:43 PM6/26/22
to jna-...@googlegroups.com
Dear Matthias,

Thank you very much for taking a look! I am sorry for being unclear in
my first e-mail and wasting your time.

So the main odgi repository is indeed still pure c++, what I am
working against is my own branch
https://github.com/JervenBolleman/odgi/blob/8e221f3d0b528aa2d65babc443105f5ffd0ef27d/src/odgi-api.h#L49
Where the header is extern "C" for the two methods I am testing.


And the JNA calling works, as in I manage to activate the specific
load_odgi_graph(const char* lines) method, via
the java code public Pointer odgi_load_graph(String filePath);

https://github.com/JervenBolleman/odgi4j/blob/0ae8c7f844dbe8233b3a5d72c91547127eafb4cf/src/main/java/swiss/sib/swissprot/odgi4j/Odgi4j.java#L12
However, I don't get the value of my java string. but what seems to be
a 64bit value e.g. (x�<hx) if I print this 'const cahr* filen' to
std::cerr.

I verified this with gdb and then I also get the same kind of value.
So I thought it might have been
that I got the equivalent of const char** but gdb seems to say no.

Now, I really am not an expert in the C++ side, so I was wondering if
the use of dynamically loaded jemalloc might be a cause.

If it adds a clue, I had a similar error the other way round (C++ -> java).
https://github.com/JervenBolleman/odgi/blob/8e221f3d0b528aa2d65babc443105f5ffd0ef27d/src/odgi-api.h#L119

Which now points to

https://github.com/JervenBolleman/odgi/blob/8e221f3d0b528aa2d65babc443105f5ffd0ef27d/src/odgi-api.cpp#L317

const char* odgi_c_version(){
return Version::VERSION.c_str();
}

But when that method was

const char* odgi_c_version(){
std::string str = odgi_version();
return str.c_str();
}

It gave a similar strange value. Only the method where I access the
field directly, or did

const char* odgi_c_version(){
std::string str = odgi_version();
char* ca = new char[str.size()+1];
std::copy(str.begin(), str.end(), ca);
ca[str.size()] = '\0';
return ca;
}

Did I get a java string back via the java/jna method at

https://github.com/JervenBolleman/odgi4j/blob/0ae8c7f844dbe8233b3a5d72c91547127eafb4cf/src/main/java/swiss/sib/swissprot/odgi4j/Odgi4j.java#L10

So maybe there is a similar issue from java -> c++

I note that I do load jemalloc in the JNA code. This is to make sure I
get jemalloc 4.5* as the 5.0 builds fail when opening otherwise :(

Regards,
Jerven
> --
> You received this message because you are subscribed to the Google Groups "Java Native Access" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/jna-users/582d06bec5f794384cb3126f9d7e1dad590a68e0.camel%40doppel-helix.eu.



--
Jerven Bolleman
m...@jerven.eu

Matthias Bläsing

unread,
Jun 27, 2022, 2:57:34 PM6/27/22
to jna-...@googlegroups.com
Hi Jerven,

so I experimented a bit - this is my result:

I replaced the contents of odgi_load_graph with a plain printf and
could reproduce your findings, but then I continued and had a look at
the parameters and return type and noticed, the return type.

It is:

ograph_t

which is defined as:

typedef std::shared_ptr<graph_t> ograph_t;

This is not a plain C type and indeed, once I replaced that with a void
or void* behavior became sane. I don't know what a std::schard_ptr is
really, but I suspect its a structure and because of this the stack
layout libffi created was not what the function invocation expected.

You need to get hold of the raw pointer and pass that.


For the version I suspect, that you see the C++ runtime cleaning up
after itself. I don't know when C++ makes a copy of the value and frees
it and indeed this might become a problem. I have to working versions:

const char* odgi_c_version(){
// Version::VERSION is const, so passing the pointer to the C string should
// be save. Of course it must not be freeed then!
printf("Version: %s\n", Version::VERSION.c_str());
fflush(stdout);
return Version::VERSION.c_str();
}

As commented this relies on noone modifying Version::VERSION, once a
modification is done, the result from ::c_str becomes invalid (see
documentation of string::c_str).

That gives me:

Version: 8e221f3
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.13
sec <<< FAILURE!
testVersionNotNull(swiss.sib.swissprot.odgi4j.Odgi4jTest) Time
elapsed: 0.015 sec <<< FAILURE!
java.lang.AssertionError: 8e221f3
at org.junit.Assert.fail(Assert.java:89)


While the output looks like gibberish, IMHO it is sane:

- Native and Java agree on the value (first line comes from native,
the AssertionError holds the Java value)
- it matches the short hash of the checked out revision I build odgi 
from


An alternative version to the above would be:

const char* odgi_c_version(){
auto version = odgi_version();
char * cstr = (char*) malloc(version.length()+1);
std::strcpy (cstr, version.c_str());
printf("Version: %s\n", cstr);
fflush(stdout);
return cstr;
}

Which gives me the same result as the first variant. However to not
leak memory the returned value must be freed by the caller:

Native.free(Pointer.nativeValue(versionPointer));


TL;DR: This works, although the asserts are wrong.

Hope that helped

Matthias

Jerven Bolleman

unread,
Jun 27, 2022, 3:15:47 PM6/27/22
to jna-...@googlegroups.com
Mathias,

Thank you so much, this helps a lot indeed! I am going to take all
your valuable insights and turn it into something nice :)

Regards,
Jerven
> --
> You received this message because you are subscribed to the Google Groups "Java Native Access" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/jna-users/b5c5c8a152e35be54cfeb97a12c9d18d09744487.camel%40doppel-helix.eu.



--
Jerven Bolleman
m...@jerven.eu
Reply all
Reply to author
Forward
0 new messages