31

似乎大多数与 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;

这样,我就不必通过传递名称和奇怪的签名字符串来手动完成获取方法 ID 的工作,并且可以避免因调用方法的未经检查的 API 引起的编程错误。事实上,它看起来很像等效的 Java。

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

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

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

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

注意:我发现了这个现有的问题,但在那种情况下,OP 并没有我对完美的要求......

更新:关于 SWIG 的评论让我想到了这个先前的问题,这似乎表明它主要是关于相反的方向,所以不会做我想要的。

重要的

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

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

https://github.com/danielearwicker/cppjvm

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

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

4

9 回答 9

17

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

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

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

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

于 2011-09-27T21:03:04.233 回答
8

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

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

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

祝你的项目好运。要做到正确,需要做很多工作。我们在这上面花了几年时间,而且我们仍在定期改进它。

于 2011-09-28T14:25:11.840 回答
4

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

https://github.com/mo22/jnipp

它的运行时占用空间小(<30kb),管理引用,并支持生成 Java 类接口。即LocalRef> stringArray; 然后使用 stringArray[1]->getBytes() 或其他东西。

于 2015-05-05T21:03:43.617 回答
2

从 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 文件的集合,以提供对象的链接和 shell 包含,以便竞争内存分配方案一起玩好。也许可以从中吸取一些教训。

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

高温高压

于 2011-09-26T19:21:00.483 回答
1

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

于 2011-09-27T16:43:04.430 回答
1

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

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

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

  2. 作为一个微型服务器,在 TCP/IP 套接字上接受来自 C/C++ 程序的请求,并通过该套接字返回结果。这需要编写网络和序列化函数,但将 C/C++ 和 Java 进程解耦,您可以清楚地将任何问题识别为 C++ 端或 Java 端。

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

于 2011-09-26T23:40:07.207 回答
0

回答我自己的问题:

http://java4cpp.kapott.org/

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

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

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

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

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

于 2011-09-28T08:24:53.317 回答
0

由于 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

于 2011-09-26T22:54:09.713 回答
0

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

于 2011-09-26T21:01:39.677 回答