要始终自动将同一指针传递给函数,在SWIG中非常简单。例如,给定“头”文件test.h,它捕获了问题的核心部分:
struct context; // only used for pointersvoid init_context(struct context **ctx) { *ctx=malloc(1); printf("Init: %pn", *ctx); }void release_context(struct context *ctx) { printf("Delete: %pn", ctx); free(ctx); }void foo(struct context *ctx) { printf("foo: %pn", ctx); }我们可以包装它,并通过执行以下操作自动将全局上下文传递到期望的任何地方:
%module test%{#include "test.h"// this pre gets put in the generated C output from SWIG, but not wrapped:static struct context *get_global_ctx() { static struct context *ctx = NULL; if (!ctx) init_context(&ctx); return ctx;}%}%typemap(in,numinputs=0) struct context *ctx "$1=get_global_ctx();"%ignore init_context; // redundant since we call it automatically%include "test.h"这将为此设置一个类型映射
struct context *ctx,而不是从Java中获取一个
get_global_ctx()匹配的地方自动调用输入。
这足以使Java开发人员能够使用一个明智的接口,但是它并不理想:它强制上下文成为全局上下文,并且意味着没有Java应用程序可以一次使用多个上下文。
考虑到Java是一种OO语言,一种更好的解决方案是使上下文成为一流的Object。我们也可以让SWIG为我们生成这样的接口,尽管它有些复杂。我们的SWIG模块文件变为:
%module test%{#include "test.h"%}// These get called automatically, no need to expose:%ignore init_context;%ignore delete_context;// Fake struct to convince SWIG it should be an object:struct context { %extend { context() { // Constructor that gets called when this object is created from Java: struct context *ret = NULL; init_context(&ret); return ret; } ~context() { release_context($self); } }};%include "test.h"我们可以成功地执行以下代码:
public class run { public static void main(String[] argv) { System.loadLibrary("test"); context ctx = new context(); // You can't count on the finalizer if it exits: ctx.delete(); ctx = null; // System.gc() might also do the trick and in a longer // running app it would happen at some point probably. }}给出:
Init: 0xb66dab40Delete: 0xb66dab40
在动态类型的语言中,这将是困难的部分-我们可以根据需要使用一种形式或另一种形式的元编程来插入成员函数。这样我们就能说出
newcontext().foo();完全像预期的那样。尽管Java是静态类型的,所以我们还需要更多。我们可以通过多种方式在SWIG中进行此操作:
接受我们现在可以
test.foo(new context());
很开心地调用-尽管它仍然看起来很像Java中的C,所以如果您最终编写了很多看起来像C的Java,我建议它可能是一种代码味道。使用
%extend
至(手动地)添加方法进上下文类,则%extend
在test.i变为:%extend {context() { // Constructor that gets called when this object is created from Java: struct context *ret = NULL; init_context(&ret); return ret;}~context() { release_context($self);}void foo() { foo($self);}}
与一样
%extend
,但是使用typemap在Java端写胶水:%typemap(javapre) struct context %{public void foo() {
$module.foo(this);
}
%}
(注意:这需要在接口文件中足够早才能起作用)
请注意,这里没有向我展示SWIG上下文结构的真实定义-它在任何需要真实定义的地方 都始终 遵循我的“库”,因此不透明指针保持完全不透明。
init_context使用双指针包装的更简单解决方案是使用
%inline提供仅在包装器中使用的额外功能:
%module test%{#include "test.h"%}%inline %{ struct context* make_context() { struct context *ctx; init_context(&ctx); return ctx; }%}%ignore init_context;%include "test.h"足以让我们编写以下Java:
public class run { public static void main(String[] argv) { System.loadLibrary("test"); // This object behaves exactly like an opaque pointer in C: SWIGTYPE_p_context ctx = test.make_context(); test.foo(ctx); // important otherwise it will leak, exactly like C test.release_context(ctx); }}替代方法,但类似的方法包括使用cpointer.i库:
%module test%{#include "test.h"%}%include <cpointer.i>%pointer_functions(struct context *,context_ptr);%include "test.h"然后可以将其用作:
public class run { public static void main(String[] argv) { System.loadLibrary("test"); SWIGTYPE_p_p_context ctx_ptr = test.new_context_ptr(); test.init_context(ctx_ptr); SWIGTYPE_p_context ctx = test.context_ptr_value(ctx_ptr); // Don't leak the pointer to pointer, the thing it points at is untouched test.delete_context_ptr(ctx_ptr); test.foo(ctx); // important otherwise it will leak, exactly like C test.release_context(ctx); }}还有一个
pointer_class宏,它比该宏多一些,也许值得使用。但是,重点是您要提供与SWIG用于表示其不了解的指针的不透明指针对象一起使用的工具,但要避免
getCPtr()本质上颠覆类型系统的调用。



