通过 JNA 调用本机 C++ 函数的非确定性 return 值(字符串类型)

Non-deterministic return values (string type) from calling native C++ functions through JNA

问题描述:

我的项目是JAVA,但我想调用一个C++函数,它以一个字符串作为输入,并在处理后输出另一个字符串。我用JNA来做这个工作,String和char*是从JAVA到C++的映射类型。(在处理过程中,我需要std::string作为中间类型,详细给出了简化代码。 )

当输入的字符串不长时,程序运行良好。然而,当我增加字符串的长度时(这里,我将 "test sentence..." 复制了 6 次),事情开始变得奇怪了。我可以看到字符串在 C++ 程序中传递(成功打印出来),但是我在 JAVA 中收到一个空字符串,这意味着它无法 return 字符串。更重要的是,每次执行while循环的次数是不同的(break循环时打印出不同的计数)。

然后,

1.长度有什么问题? JNA有什么限制吗?

2。我能达到我的目标吗?如何正确地在 JNA 中传递字符串?(顺便说一句,我试过指针的东西,但它不起作用)

先谢谢了,这是我的代码和一些环境细节

C++ 端的代码>>>>>>>>>>>>test.cpp

#include<iostream>
using namespace std;
extern "C" __attribute__((visibility("default"))) const char* SeudoCall(const char* input); 
const char* SeudoCall(const char* str){
    std::string input(str);
    std::cout<<"in C:"<<input<<std::endl;
    return input.c_str();
}

编译 C++ 库>>>>>>>>>>

g++ test.cpp -fpic -shared -o libtest.so

代码在 JAVA 侧>>>>>>>>>>>>

import com.sun.jna.Library;
import com.sun.jna.Native;
public class TestSo {
    public interface LgetLib extends Library {
        LgetLib INSTANCE = (LgetLib) Native.loadLibrary("test", LgetLib.class);
        String SeudoCall(String input);
    }
    public String SeudoCall(String input){
        return LgetLib.INSTANCE.SeudoCall(input);
    }
    public static void main(String[] args) {
        TestSo ts = new TestSo();
        String str = "test sentence...test sentence...test sentence...test sentence...test sentence...test sentence...";
        String retString;
        int count=0;
        while(true){
            System.out.println("count:"+(++count));
            retString=ts.SeudoCall(str);
            System.out.println("in JAVA:"+retString);
            if(retString.equals("")){
                System.out.println("break");
                break;
            }
        }
    }
}

运行 详细>>>>>>>>>>>>

Intel Core i7-4790 3.60GHz
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
JNA 4.1.0
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

您的 C++ 代码马上就有问题了。您正在 return 指向局部 std::string 的指针,该指针将在函数存在时被销毁。因此,您正在引入未定义的行为。

换句话说,即使应用程序都是 C++,你的函数也会有错误,所以它不仅仅是一个 Java/JNA/C++ 问题,而是 C++ 本身的问题。

const char* SeudoCall(const char* str){
    std::string input(str);
    std::cout<<"in C:"<<input<<std::endl;
    return input.c_str();  // <-- this is no good.
}

c_str() 绑定到局部变量 input。当函数退出时,input 的析构函数发生,同时带走代表字符串的 c_str()

要解决此问题,您必须 return 将字符串返回到 Java,这将是 "alive" 在函数 return 之后。我不知道 JNA,但对于 JNI,您可以使用 JNI NewStringUTF 函数创建一个字符串,该字符串将被 return 编辑为 jstring

也许这篇 link 会对您有所帮助:

How to return char * aarr= new char[200] from C++ to Java using JNA

The program runs well, when the input string is not long. However, when I increase the length of the string(here, I duplicate "test sentence..." for 6 times), things began strange. I can see the string is pass in C++ program(successfully print out), BUT I received an empty string in JAVA, which means it fails to return the string. What's more, the times of while loop is different on each excution(print out different count when break the loop).

您看到的是未定义的行为。从您的 C++ 函数 中得到的指针 return 可能 指向保存字符的内存,但它并不可靠,正如您的测试所显示的那样。