你在 carrays.i 的正确路线上,虽然通常我更喜欢使用%array_class
而不是%array_functions
在有选择的情况下使用。问题是您的 array_functions/array_class 为您提供了一个SWIGTYPE_p_unsigned_char
(即指针的代理unsigned char
),而您需要一个void
指针,并且由于 Java 中的强类型化,没有简单的方法可以在两者之间进行转换。我们可以解决这个问题。
为了说明这个答案,我创建了一个非常简单的头文件来重现您的问题:
typedef struct sp_session_config {
const void *application_key; ///< Your application key
} session_config;
所以问题是我们不能setApplication_key()
用 array_class/array_function 生成uint8_t
的数组调用,因为类型不匹配。
有很多可能的解决方案。一种方法可能是告诉 SWIG 忽略application_key
insp_session_config
并假装它是uint8_t* application_key
. 为此,您需要在 C 中为执行工作的包装器提供一个 set/get 函数(在这种情况下是微不足道的)并用于%ignore
隐藏“真实”成员,%extend
添加“假”成员并%rename
停止%ignore
忽略假一:
%module test
%{
#include "test.h"
#include <stdint.h>
// Internal helpers for our fake memeber
static void session_config_application_key_set(session_config *cfg, const uint8_t *arr) {
cfg->application_key = arr;
}
static const uint8_t *session_config_application_key_get(const session_config *cfg) {
return cfg->application_key;
}
%}
%include <carrays.i>
%include <stdint.i>
%array_class(uint8_t, uint8Array);
%ignore sp_session_config::application_key; // ignore the real one when we see it
%include "test.h"
%rename("%s") sp_session_config::application_key; // unignore for extend
%extend session_config {
uint8_t *application_key;
}
这足以让我们用 Java 编写:
public class run {
public static void main(String[] argv) {
session_config cfg = new session_config();
uint8Array key = new uint8Array(4);
key.setitem(0, (short)100); key.setitem(1, (short)101);
key.setitem(2, (short)102); key.setitem(3, (short)103);
cfg.setApplication_key(key.cast());
}
}
或者,如果您愿意,您可以使用类型映射系统将成员公开为uint8_t*
:
%module test
%{
#include "test.h"
%}
%include <carrays.i>
%include <stdint.i>
%array_class(uint8_t, uint8Array);
%typemap(jstype) const void *application_key "$typemap(jstype, const uint8_t*)"
%typemap(javain) const void *application_key "$typemap(jstype, const uint8_t*).getCPtr($javainput)"
%typemap(javaout) const void *application_key {
long cPtr = $jnicall;
return (cPtr == 0) ? null : new $typemap(jstype, const uint8_t*)(cPtr, $owner);
}
%include "test.h"
它仅匹配const void *application_key
并在接口中生成代码以使用包装的uint8_t*
而不是void*
.
另一种可能的方法是通过提供接口文件内部的特殊定义,简单地向 SWIG 声明该成员是a :uint8_t*
struct
%module test
%{
#include "test.h"
%}
%include <carrays.i>
%include <stdint.i>
%array_class(uint8_t, uint8Array);
typedef struct sp_session_config {
const uint8_t *application_key;
} session_config;
// Ignore the one in the header file, use our special version instead
%ignore sp_session_config;
%include "test.h"
这样做很简单,但维护起来更棘手 - 每次sp_session_config
更改时,您都必须确保更新接口文件以匹配它,否则您可能不会暴露,甚至不正确地暴露struct
.
请注意:注意这些示例中的所有权 - Java 端的数组保留所有权,因此您需要确保:
- C 库不会尝试释放它(如果释放它,你会看到一个 double
free()
)
- Java中数组的生命周期超过了它在C库中的使用。
如果需要,您可以让 C 库获得内存的所有权,这些示例中最容易做到这一点的是使用类型映射的示例。(您可以修改 javain 类型映射以获取所有权)。
最后,对于另一种可能的解决方案,您可以使用一点 JNI或更多其他技术,正如我过去回答的那样,使用 Java 数组或为此生成合适的代码。