【问题标题】:Looking for a convenient way to call Java from C++寻找一种从 C++ 调用 Java 的便捷方式
【发布时间】:2011-11-24 04:01:40
【问题描述】:

似乎大多数与 JNI(Java 本机接口)相关的文档或帮助程序库都与从 Java 调用本机代码有关。这似乎是它的主要用途,尽管它的功能更多。

我主要想朝相反的方向工作:通过添加一些 Java 库来修改现有的(相当大的)可移植 C++ 程序。例如,我想让它通过 JDBC 调用数据库,或者通过 JMS 调用消息队列系统,或者发送电子邮件,或者调用我自己的 Java 类等。但是对于原始 JNI,这是非常不愉快且容易出错的。

因此,理想情况下,我希望编写可以像 C++/CLI 调用 CLR 类一样轻松调用 Java 类的 C++ 代码。比如:

using namespace java::util::regex; // namespaces mapped

Pattern p = Pattern.compile("[,\\s]+");

array<java::lang::String> result = 
    p.split("one,two, three   four ,  five");

for (int i=0; i < result.length(); i++)
    std::cout << result[i] << std::endl;

这样,我就不必manually do the work of getting the method ID by passing the name and the weird signature strings,并且可以避免因调用方法的未经检查的 API 引起的编程错误。事实上,它看起来很像等效的 Java。

注意。我仍在谈论使用 JNI! 作为一项底层技术,它非常适合我的需求。它是“进行中的”并且非常高效。我不想在单独的进程中运行 Java 并对它进行 RPC 调用。 JNI 本身很好。我只想要一个愉快的界面。

必须有一个代码生成工具来生成等效的 C++ 类、命名空间、方法等,以完全匹配我指定的一组 Java 类所公开的内容。生成的 C++ 类将:

  • 具有接受类似包装版本的参数的成员函数,然后执行必要的 JNI 巫术来进行调用。
  • 以相同的方式包装返回值,这样我就可以以自然的方式链接调用。
  • 维护每个类的方法 ID 静态缓存,以避免每次都查找它们。
  • 完全线程安全、可移植、开源。
  • 在每次方法调用后自动检查异常并产生一个标准 C++ 异常。
  • 当我以通常的 JNI 方式编写本机方法但我需要调用其他 Java 代码时也可以使用。
  • 数组应该在基本类型和类之间完全一致地工作。
  • 毫无疑问,当引用需要在本地引用框架之外生存时,它们需要像全局这样的东西来包装引用 - 同样,所有数组/对象引用的工作方式应该相同。

这样一个免费的、开源的、可移植的库/工具是否存在,还是我在做梦?

注意:我找到了this existing question,但在那种情况下,OP 并没有我对完美的要求那么严格......

更新:关于 SWIG 的评论将我引至 this previous question,这似乎表明它主要是关于相反的方向,因此不会做我想做的事。

重要

  • 这是关于能够编写操作 Java 类和对象的 C++ 代码,而不是相反(见标题!)
  • 我已经知道 JNI 存在(见问题!)但是 JNI API 的手写代码不必要地冗长、重复、容易出错、在编译时没有类型检查等。如果你想缓存方法ID 和类对象更加冗长。我想自动生成 C++ 包装类来为我处理所有这些。

更新:我已经开始研究自己的解决方案:

https://github.com/danielearwicker/cppjvm

如果这已经存在,请告诉我!

注意。如果您正在考虑在自己的项目中使用它,请随意,但请记住,现在代码已经有几个小时了,到目前为止我只写了三个非常不费力的测试。

【问题讨论】:

  • SWIG 虽然我只在 C 和 Python 中使用过它...... 很久 以前。
  • 查看更新:SWIG 显然没有这样做。
  • 您想在哪个编译器/解释器中执行您的程序,C 还是 Java?如果是 Java,则无法使用 JNI 进行反向操作。
  • 我已经在标签中添加了 swig,以防有人可以澄清。
  • @Naved - 我不明白你的问题:我想在本机编译的 C++ 中执行 C++ 部分(像往常一样)和 JVM 中的 Java 部分,并将两者与 JNI/调用。我只是不想手动做所有的 JNI 废话。

标签: java java-native-interface swig invocation


【解决方案1】:

从 C++ 重新调用 Java。

你可以做你想做的,但你必须让 Java 来控制。我的意思是,您创建调用本机代码的 Java 线程,然后它们从那里阻塞,有点等待您的本机代码给它做点什么。您可以根据需要创建尽可能多的 Java 线程来完成足够的工作/吞吐量。

所以你的 C++ 应用程序启动了,它创建了一个 JVM/JavaVM(根据记录的方式,示例存在于 qtjambi 代码库中,见下文),这反过来执行通常的 JNI 初始化和 System.loadLibrary() 并为 JAR 提供“本机”链接。然后,您初始化一堆线程并调用一些 JNI 代码(您创建的),它们可以在其中阻塞以等待您的 C++ 代码为它们提供一些工作。

您的 C++ 代码(可能来自另一个线程)然后设置并将所需的所有信息传递给一个阻塞和等待的 Java 线程工作者,然后命令它运行,然后它可能会回到纯 Java执行工作并返回结果的代码。

...

可以从 C++ 代码设置、创建和包含 JavaVM 实例。这可以强制输入您自己的 CLASSPATH/JAR 来设置您需要封装在 C++ 程序中的包含环境。

我相信你已经在http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html找到了它的概要

...

QtJambi 项目中有一种 C++ => Java JNI 生成器(我从事并帮助维护)。这对于 Qt 工具包来说是完全定制的,但本质上它将一堆 C++ 头文件转换为 C++ .cpp/.h 文件和 *.java 文件的集合,以提供对象的链接和外壳包含这样竞争的内存分配方案就可以很好地协同工作。也许可以从中吸取一些教训。

这无疑是一个概念证明,您所要求的生成器恰好包含在 qtjambi 项目中(但可以独立于一些工作),这是 LGPL 许可的(开源)。 Qt 工具包不是一个小的 API,但它可以生成 100 多个类来覆盖高百分比的 API(>85% 和几乎 100% 的核心/GUI 部分)。

HTH

【讨论】:

  • 您关于线程的部分答案对我来说毫无意义。我已经可以做一些比使用纯 JNI 简单得多的事情了。我只是初始化JVM,让它加载我一个类,调用它的构造函数,调用它的方法。仅此而已。
  • QtJambi 库是一个允许 Java 代码使用 Qt 库的项目,即 Java 调用 C++。它是否读取现有的 Java 类并生成相应的 C++ 包装类,以便可以轻松地从 C++ 调用 Java 类?
  • 线程问题是关于如何设置一个进程,以便 C++ 可以随时安全地调用 Java。 Qt 库的某些部分很复杂,它是一个 C++ 库,也需要回调到 Java 中。您可以注册在 C++ 中运行但偶尔在 Java 中调用的回调/事件处理程序/线程。 Java 在纯 Java 和 C/C++ 中都有 API。但是 C/C++ 没有用于在 Java 中做事的 API。当 Java 已经有 C++ API(即 JNI)时,为 Java 事物制作 C++ 包装器是没有意义的。
  • 也许我的问题措辞非常糟糕或其他什么。
  • 这是一个非常有趣的想法,它似乎只需要对c++部分进行一些重构,但其余大部分是基于非常稳定的纯JNI。很高兴看到一些真实的例子,也许其他人已经尝试过?
【解决方案2】:

对于这颗钉子来说可能有点太大了,但这不是 CORBA 的用途吗?

【讨论】:

  • 来自该链接:“C++ 映射非常困难;映射要求程序员学习复杂且令人困惑的数据类型,这些数据类型早于 C++ 标准模板库”听起来很棒! :)
【解决方案3】:

由于 CORBA 似乎不是您想要的,这里有描述如何从 C/C++ 启动 JVM 和调用 JAVA 方法的链接。

http://java.sun.com/docs/books/jni/html/invoke.htmlhttp://java.sys-con.com/node/45840

PS:在将 Java 与 C++ 连接时,您还应该查看 JNAbridj

【讨论】:

  • 时间不早了。这两个链接似乎提供的信息与其他人已经提供的信息相同......晚安:)
【解决方案4】:

我也遇到了很多困难 让 JNI 处理不同的工作 操作系统,应对 32/64 位架构并确保找到并加载了正确的共享库。 我发现 CORBA(MICO 和 JacORB)也很难使用。

在这种情况下,我没有发现从 C/C++ 调用到 Java 的有效方法以及我首选的解决方案 将我的 Java 代码运行为:

  1. 独立程序 我可以轻松地从 C/C++ 程序运行 与java -cp myjar.jar org.foo.MyClass。我想这对你的情况来说太简单了。

  2. 作为迷你服务器,接受来自 C/C++ 的请求 TCP/IP 套接字上的程序和 也通过此套接字返回结果。 这需要编写网络和序列化函数 但解耦了 C/C++ 和 Java 进程,你可以清楚地 将任何问题确定为 C++ 端或 Java 端。

  3. 作为 Tomcat 中的 Servlet 并从我的 C/C++ 发出 HTTP 请求 程序(其他 servlet 容器也可以工作)。 这也需要编写网络和序列化函数。 这更像是 SOA。

【讨论】:

  • 谢谢。到目前为止,我已经让 JNI 在 Windows 和 Linux 上运行没有问题 - 看起来基本相同。 CORBA 与我需要的东西相去甚远。在进程外编组所有调用似乎是您使用 SOA(或 CORBA)所获得的东西的手写版本,所有这些都有很大的额外开销和更多的复杂性。
  • 同时,JNI 将是完美的轻量级解决方案,只要每个方法调用的咒语(传递方法名称和签名,缓存 ID,确定 Call[Static]MethodT 的正确风格,检查异常) 可以完全自动化。这就是我想要的。
【解决方案5】:

使用ThriftProtocol Buffers 来促进Java 到C++ 的调用怎么样?

【讨论】:

  • 我应该更新我的问题以排除使用 RPC 到进程外服务器。
【解决方案6】:

是的,现有的工具可以做到这一点——为 Java 类生成 C++ 包装器。这使得在 C++ 中使用 Java API 更加透明和愉快,成本和风险更低。

我用得最多的是JunC++ion。它成熟、强大且稳定。主要作者人很好,反应也很灵敏。不幸的是,它是一种商业产品,而且价格昂贵。

Jace 是具有 BSD 许可证的免费开源工具。自从我上次和杰斯一起玩已经有好几年了。看起来还有一些积极的发展。 (我还记得十多年前原作者的 USENET 帖子,问的问题基本上和你问的一样。)

如果您需要支持从 Java 到 C++ 的回调,定义实现 Java 接口的 C++ 类会很有帮助。至少 JunC++ion 允许您将此类 C++ 类传递给接受回调的 Java 方法。我上次尝试 jace 时,它​​不支持这一点——但那是七年前的事了。

【讨论】:

  • 谢谢。我从 Jace 的源代码中看到,它将每一个引用都变成了全局!这不是一个好主意。 JNI 特意提供了一个选项:全局变量比局部变量更重要,而实际程序中的绝大多数变量都是局部变量。由于 JVM 会处理它,因此不需要在析构函数中“释放”本地变量。你只需要一个 RAII 类来封装PushLocalFrame/PopLocalFrame。 JunC++ion 对此采取了什么方法?
  • 顺便说一句,我在 Google 上搜索术语 C++ Java JNI Wrapper,我现在看到 JunC++ion 在结果的第 22 页上!还没发现杰斯……
  • 如果搜索基于质量,JunC++ion 将位于第 1 页。我现在无法访问 JunC++ion。我猜想全局引用在某些情况下是必要的,而对其他人来说就足够了,但这只是一个猜测。我看看能不能让作者回复一下。
  • Alex Krapf 下面的回答涉及全球/本地参考。
【解决方案7】:

回答我自己的问题:

http://java4cpp.kapott.org/

似乎不是一个活跃的项目。作者建议不要与 JDK 1.5 或更高版本一起使用。

它似乎有一个严重的问题:它将裸指针传递给它的包装对象:

java::lang::Integer* i = new java::lang::Integer("10");

delete i; // don't forget to do this!

它还导致了一个更微妙的问题,即为了表示赋值兼容性(例如,一个类作为它实现的接口的子类型),包装器必须相互继承。

【讨论】:

    【解决方案8】:

    我是 Codemesh 语言集成产品(包括 JunC++ion)的主要架构师之一。自 1999 年以来,我们一直在进行这种集成,并且效果非常好。最大的问题不是 JNI 部分。 JNI 很繁琐且难以调试,但是一旦你做对了,它基本上就可以继续工作了。有时,您会因 JVM 或操作系统更新而崩溃,然后您必须微调您的产品,但总的来说它是稳定的。

    最大的问题是类型系统映射以及一般可用性和目标解决方案之间的权衡。例如,您声明您不喜欢 JACE 将所有对象引用视为全局变量这一事实。我们做同样的事情(有一些逃生舱口),因为事实证明这是最适合 95% 的客户的行为,即使它会损害性能。如果您要发布 API 或产品,您必须选择让大多数人都能正常工作的默认设置。选择本地引用作为默认选项是错误的,因为越来越多的人正在编写多线程应用程序,而且人们想从其他语言中使用的许多 Java API 本质上都是多线程的,带有异步回调等。

    我们还发现您确实希望为人们提供基于 GUI 的代码生成器来创建集成规范。一旦他们指定了它,您就可以使用 CLI 版本将其集成到夜间构建中。

    祝你的项目好运。要做到正确,需要做很多工作。我们花了好几年的时间来改进它。

    【讨论】:

    • 谢谢。回复:默认值的明智选择,我完全同意 - 这是设计 IMO 最重要的事情。对于我目前的项目,选择使用局部变量很重要,共享数据将是非常少数,所以我将分别使用 java::lang::Stringglobal&lt;java::lang::String&gt; 作为局部变量和全局变量的语法。
    【解决方案9】:

    我遇到了几乎相同的问题,最后我自己做了,也许这对某人有帮助。

    https://github.com/mo22/jnipp

    它具有较小的运行时占用空间 ( 字符串数组;然后使用 stringArray[1]->getBytes() 什么的。

    【讨论】:

      猜你喜欢
      • 2010-10-10
      • 2013-01-17
      • 2010-10-10
      • 1970-01-01
      • 2018-06-25
      • 2017-10-02
      • 1970-01-01
      • 2012-11-05
      • 1970-01-01
      相关资源
      最近更新 更多