使用 SWIG 将 C++ 自定义单元类型转换为原始 Java 类型

ble*_*euj 5 c++ java swig

我有 C++ 代码,正在尝试使用 SWIG 提供 Java 绑定。

struct meters_t : public boost::units::quantity<boost::units::si::length> {};
Run Code Online (Sandbox Code Playgroud)

我希望尝试将所有引用替换meters_t为doubleor Java.lang.Double。

我在我的 SWIG 接口文件中尝试了以下操作.i:

%typemap(jstype) meters_t "java.lang.Double"
%typemap(jtype) meters_t "java.lang.Double"
%typemap(jni) meters_t "jdouble"
...
// Duplicated for const, const&, &
%ignore meters_t
%include "my_include.h"
Run Code Online (Sandbox Code Playgroud)

但是我目前遇到类型转换错误

error C2440: 'type cast': cannot convert from 'meters_t *' to 'jdouble'
error C2440: 'type cast': cannot convert from `jdouble` to `meters_t *`
Run Code Online (Sandbox Code Playgroud)

有人有任何建议来纠正这个问题吗?我很快就失去了将可能转换meters_t为简单的想法的希望:/double

更新: 我还添加了以下内容

%typemap(javain) meters_t "$javainput"
Run Code Online (Sandbox Code Playgroud)

这使得能够构建 Java 绑定,但是当我尝试使用 Java 绑定时(在另一个作为构造函数struct参数的情况下meters_t),它会立即抛出错误

A fatal error has been detected by the Java Runtime Environment
EXCEPTION_ACCESS_VIOLATION
Run Code Online (Sandbox Code Playgroud)

更新2.0

我现在已经meters_t封装了 SWIG。我的新问题是一个带有 boost 默认参数的类,我遇到了难以捉摸的Syntax error in input(3).错误

Foo(meters_t meters = meters_t{ 0.0 * boost::units::si::meters });
Run Code Online (Sandbox Code Playgroud)

Von*_*onC 1

您已经开始使用typemap走上正确的轨道。这些对于 C++ 和 Java 之间的类型转换至关重要。但这些错误表明 SWIG 在自动转换meters_t和之间遇到问题jdouble。

\n

您可能需要提供自定义类型映射来显式处理此转换。您需要为 SWIG 提供一种方法来了解如何进行转换,meters_t反之亦然jdouble。

\n

尝试并实施%typemap(in)和%typemap(out)for meters_t。这些类型映射将定义从 C++ 传递到 Java 时如何将meters_t对象转换为 Java double,反之亦然。

\n

SWIG 对解析复杂默认参数的支持有限]。一种解决方案是简化 SWIG 接口文件中的默认参数,或者提供没有默认参数的构造函数的重载版本以供 SWIG 包装。

\n

在您的SWIG 接口文件中,实现类型映射:

\n
%typemap(in) meters_t {\n    // Code to convert from jdouble to meters_t\n}\n\n%typemap(out) meters_t {\n    // Code to convert from meters_t to jdouble\n}\n\n// For the constructor issue\n%ignore Foo::Foo(meters_t);\n%extend Foo {\n    Foo() {\n        return new Foo(meters_t{0.0 * boost::units::si::meters});\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您需要根据如何meters_t与double.

\n
Java Side                     JNI Bridge                    C++ Side\n\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80                     \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80                    \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n   jdouble                         |                     meters_t Object\n   (double)                        |                           |\n     |                             |                           |\n     |      [ JNI Conversion ]     |                           |\n     |---------------------------->|                           |\n     |                             |    Construct meters_t     |\n     |                             |  using jdouble value      |\n     |                             |<--------------------------|\n     |                             |                           |\n     |                             |      Extract double       |\n     |                             |  value from meters_t      |\n     |                             |-------------------------->|\n     |                             |                           |\n     |<----------------------------|                           |\n     |                             |                           |\n
Run Code Online (Sandbox Code Playgroud)\n
\n

关于“ Code to convert from jdouble to meters_t”,从Java转换为C++时,您将收到一个Java double(以C++ JNI代码表示jdouble)。您需要将其转换为meters_t对象。
\n假设meters_t可以从 a 构造或分配double,类型映射可能如下所示:

\n
%typemap(in) meters_t {\n    $1 = meters_t($input * boost::units::si::meter);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在此类型图中:

\n
    \n
  • $1是 C++ 参数的占位符(在本例中为meters_t)。
  • \n
  • $input表示来自 Java 的输入 (a jdouble)。
  • \n
  • 该表达式从 Java double$input * boost::units::si::meter构造一个对象,假设可以通过这种方式初始化。meters_tmeters_t
  • \n
\n
\n

为了 ” Code to convert from meters_t to jdouble”:

\n

从 C++ 转换为 Java 时,您需要double从meters_t对象中提取值:

\n
%typemap(out) meters_t {\n    $result = (jdouble)$1.value();\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在此类型图中:

\n
    \n
  • $1是 C++ 的占位符meters_t对象的占位符。
  • \n
  • $result是您要映射到的 Java 类型的 JNI 表示形式(jdouble在本例中)。
  • \n
  • $1.value()double是从对象中提取值的假设方法或方式meters_t。您需要将其替换为从类型中提取数值的实际方法或方法meters_t。
  • \n
\n

确保meters_t可以从 a 构造double并且具有将其值提取为 a 的方法double。如果没有,您将需要调整代码以适应 的实际界面meters_t。
\n这些类型映射假定boost::units::si::meter是适当的转换单位。如有必要,请根据您的具体实现进行调整meters_t。

\n
\n
\n

不幸的是,这并没有纠正我当前的未决问题。我实际上已经尝试过您所建议的忽略构造函数并提供另一个构造函数。
\n C:\\my_project\\src\\inc\\foo.h(line of the foo ctor): Syntax error in input(3)。鉴于错误位于 中foo.h,我觉得 SWIG 无论出于何种原因都无法解析默认参数,忽略代码将无济于事;我想 SWIG 读取 foo.h 独立于 SWIG 接口文件中的任何指令?

\n
\n

该错误Syntax error in input(3)确实表明 SWIG 在解析头文件中的 C++ 代码时遇到问题,特别是在foo.h. 当 C++ 代码中使用的语法或结构对于 SWIG 来说过于复杂或不熟悉时,就会发生这种情况。

\n

如果可能,请简化 C++ 头文件中的构造函数。这意味着以 SWIG 可以理解的方式删除或更改默认参数。例如,您可以将构造函数分成两部分:一个不带参数,另一个带参数meters_t,在实现中而不是在声明中处理默认情况。

\n
// Original:\n// Foo(meters_t meters = meters_t{ 0.0 * boost::units::si::meters });\n\n// Simplified:\nFoo();\nFoo(meters_t meters);\n
Run Code Online (Sandbox Code Playgroud)\n

专门为 SWIG 创建构造函数的重载版本。该版本不会有复杂的默认参数。使用SWIG 的%ignore指令忽略有问题的构造函数并仅公开简化版本。

\n

在您的 SWIG 接口文件中:

\n
%ignore Foo::Foo(meters_t meters = meters_t{ 0.0 * boost::units::si::meters });\n\n%extend Foo {\n    Foo() {\n        return new Foo(meters_t{0.0 * boost::units::si::meters});\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您还可以%inline直接在接口文件中提供有问题的代码的替代、SWIG 友好版本。这允许您按原样保留原始 C++ 代码,但提供简化版本供 SWIG 处理。

\n
%inline %{\nclass Foo {\n    public:\n    Foo() : Foo(meters_t{0.0 * boost::units::si::meters}) {}\n    Foo(meters_t meters) { /* Constructor implementation */ }\n};\n%}\n
Run Code Online (Sandbox Code Playgroud)\n

关键是创建一个 SWIG 可以解析的接口,而不会丢失 Java 绑定中所需的功能。

\n
\n
\n

是的,修改ctor来提供Foo()和Foo(meters_t meters),移动初始化Foo::meters (meters = meters_t{ 0.0 * boost::units::si::meters })来Foo.cpp纠正问题。
\n我真的更喜欢 SWIG 包装有问题的代码,而不必更改源代码。
\n我觉得我缺少一个 boost 标头,但我已将文件中使用的包含添加.h到我的 SWIG 界面中%module MyModule { //some boost headers }。简而言之,SWIG 不知道boost::units::si::meter它读取时是什么Foo.h什么!?

\n
\n

我同意:您不希望修改原始 C++ 源代码来适应 SWIG。该问题(SWIG 无法识别boost::units::si::meter)可能是由于 SWIG 对复杂 C++ 结构的理解有限,尤其是来自Boost等库的结构。

\n

在 SWIG 接口文件中包含 Boost 标头是一个好的开始。然而(再次),SWIG 可能无法完全理解或解析复杂的 Boost 结构。确保在 SWIG 接口文件中包含必要的 Boost 标头,类似于将它们包含在 C++ 标头文件中的方式(尽管对于 Boost 中的复杂模板类型来说,这可能并不总是可行)。

\n
%module MyModule\n%{\n#include "foo.h"\n#include <boost/units/systems/si/length.hpp>\n#include <boost/units/systems/si/prefixes.hpp>\n%}\n
Run Code Online (Sandbox Code Playgroud)\n

如前所述,用于%ignore从 SWIG 的处理中排除有问题的构造函数。
\n然后,要么用于%extend定义构造函数的 SWIG 友好版本,要么%rename向 Java 公开不同的简化构造函数。

\n
%ignore Foo::Foo(meters_t meters = meters_t{ 0.0 * boost::units::si::meters });\n
Run Code Online (Sandbox Code Playgroud)\n

作为最后的手段,如果 SWIG 难以处理 Boost 类型,请考虑在 C++ 中创建一个包装类,为 SWIG 提供更简单的接口。该包装器将在内部处理 Boost 类型和复杂性,仅公开 SWIG 友好的类型和方法。

\n
    \n
  • 创建一个FooWrapper简化 SWIG 接口的类。
  • \n
  • 在内部FooWrapper,管理复杂的 Boost 交互,为 SWIG 公开更简单的方法。
  • \n
\n
class FooWrapper {\npublic:\n    FooWrapper() : foo(0.0 * boost::units::si::meters) {}\n    explicit FooWrapper(double meters) : foo(meters * boost::units::si::meters) {}\n\nprivate:\n    Foo foo;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

然后在 SWIG 接口文件中:

\n
%include "FooWrapper.h"\n
Run Code Online (Sandbox Code Playgroud)\n

我们的目标仍然是在保留原始 C++ 源代码和为 SWIG 提供它可以理解的形式之间找到平衡。在这种情况下,为 SWIG 创建简化的接口或包装器通常是一个实用的解决方案,特别是在处理 Boost 这样的复杂库时。

\n