带有指针的C结构,如何Swig?

Posted

技术标签:

【中文标题】带有指针的C结构,如何Swig?【英文标题】:C structure with pointers, how to Swig? 【发布时间】:2014-08-13 14:55:48 【问题描述】:

我正在尝试使用 Swig 为一些内部 C 代码生成包装器,以便我可以将它重用于新的 android java 项目并且遇到问题。我是 Java 和 Swig 的新手,所以请在任何回复的技术内容方面对我温和。

我正在尝试包装一个名为 S_MESSAGE_STRUCT 的 C 结构,该结构包含指针,使得它的元素可以从 java 以及遗留 C 代码访问(设置、写入、读取等)。我定义结构的 C 头文件如下,代码是示例性的,并从我的真实代码(具有相同的问题)中简化:

#ifndef MESSAGE_H_
#define MESSAGE_H_

#include <stdbool.h>

typedef struct

    int*    i1;
    char*   c1;
    int     len;
 *P_S_MESSAGE_STRUCT, S_MESSAGE_STRUCT;

bool t_func(P_S_MESSAGE_STRUCT p_s_mystruct);

#endif /* ndef MESSAGE_H_ */

我的 C 文件包含一个函数,它测试 S_MESSAGE_STRUCT 的 int* i1 元素,然后写入 c1 和 i1 元素,如下所示:

#include "message.h"

bool t_func(P_S_MESSAGE_STRUCT p_s_mystruct)

    if (*p_s_mystruct->i1 == 1)
    
        strcpy(p_s_mystruct->c1, "Hello from swig");
        p_s_mystruct->c1[p_s_mystruct->len-1] = '\0';
        *p_s_mystruct->i1 = strlen(p_s_mystruct->c1);
    

    return true;

我正在使用 .i 文件,如下所示:

/* File : Message.i */
%module Message
%
/* Includes the header in the wrapper code */
#include "../../../common/message/message.h"
%

// Enable the JNI class to load the required native library.
%pragma(java) jniclasscode=%
 static 
 try 
 java.lang.System.loadLibrary("Message");
  catch (UnsatisfiedLinkError e) 
 java.lang.System.err.println("native code library failed to load.\n" + e);
 java.lang.System.exit(1);
 
 
%

%include <typemaps.i>
%apply signed char * INOUT char*;

typedef struct

    int*      i1;
    char*   c1;
    int       len;
 *P_S_MESSAGE_STRUCT, S_MESSAGE_STRUCT;

%include cpointer.i

bool t_func(P_S_MESSAGE_STRUCT p_s_mystruct);

%pointer_functions(int, intp);

我的 java 源函数如下(从默认的 Android Activity 编辑):

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

/*  typedef struct
    
            int* i1;
            char* c1;
            int len;
    *P_MYSTRUCT, MYSTRUCT;
*/
    S_MESSAGE_STRUCT p_my_struct = new S_MESSAGE_STRUCT();

    int i1 = 1;
    SWIGTYPE_p_int i1p = Message.new_intp();
    Message.intp_assign(i1p, i1);

    int len = 128;
    byte[] c1 = new byte[len];

    p_my_struct.setI1(i1p);
    p_my_struct.setC1(c1);
    p_my_struct.setLen(len);

    Message.t_func(p_my_struct);
    Integer i1_ret = Message.intp_value(i1p);

    byte[] ret_c1 = p_my_struct.getC1();

    String display = new String();
// !!        display = Arrays.toString(ret_c1); // <<<< Gives Exception !!
    display += i1_ret;

    final EditText eText = (EditText) findViewById(R.id.editText1);

    eText.setText(display);

一切都可以编译和构建,并且 swig 调用没有错误:

swig -java -package com.mobbu.Message -outdir ../../src/com/mobbu/Message/ -verbose Message.i

但我知道我做错了什么,因为在我从 java 调用 t_func() 函数后,我不断遇到问题。问题多种多样,包括在调用 t_func() 完成后程序挂起,但有时程序运行并显示输出 OK。当显示输出时,由于 c1 的贡献为空(它只给出输出“15”,没有“Hello from swig”的迹象。

我已经确定程序不稳定源于对 P_MYSTRUCT 结构中的指针元素 i1 和 c1 的写入,因为没有这些元素或没有写入都没有问题(但将 c1,i1 元素留在结构中)。

我将非常感谢有关如何实现我能够使用 C 结构的指针元素的目标的帮助,以及我在 Message.i 文件中做错了什么的任何想法?我正在使用 SWIG 版本 3.0.2。 谢谢,

迈克

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!编辑:

我发现如果我在 message.i 中更改以下行

%apply signed char * INOUT char*;

%apply byte * INOUT char*;

我的 Swig 调用出现以下错误:

Warning 453: Can't apply (byte *INOUT). No typemaps are defined.

但是,生成的代码可以编译并运行正常,但条件是我需要更改我的 java 应用程序代码以使用 String 而不是 byte[] 作为其 c1 版本。

我的新 java 代码是:

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

/*  typedef struct
    
            int* i1;
            char* c1;
            int len;
    *P_MYSTRUCT, MYSTRUCT;*/
    S_MESSAGE_STRUCT p_my_struct = new S_MESSAGE_STRUCT();

    int i1 = 1;
    SWIGTYPE_p_int i1p = Message.new_intp();
    Message.intp_assign(i1p, i1);

    int len = 128;
    byte[] c1 = new byte[len];

    String s1 = new String(c1);
    p_my_struct.setI1(i1p);
    p_my_struct.setC1(s1);
    p_my_struct.setLen(len);

    Message.t_func(p_my_struct);

    SWIGTYPE_p_int pi1_ret = p_my_struct.getI1();
    Integer i1_ret = Message.intp_value(pi1_ret);
    String display = p_my_struct.getC1();

    display += i1_ret;

    final EditText eText = (EditText) findViewById(R.id.editText1);

    eText.setText(display);

这是否说明我可能做错了什么?

感谢您的任何想法,

迈克

【问题讨论】:

我没有看到指针i1c1 被分配了内存位置的地址。我看到您在哪里使用 setI1()setC1() 并使用 setC1() 将 Java 句柄传递给 Java 数组,但是我不希望 Java 内存句柄与 C 指针地址相同。 这个 SWIG 页面SWIG with C Pointers 也可以提供帮助。 还可以查看Use SWIG to Handle C Returning a pointer to an array in Java 以及Using SWIG with pointer to function in C struct 和Wrapping C function with pointer arguments using SWIG。 @RC: int i1 = 1; SWIGTYPE_p_int i1p = Message.new_intp(); Message.intp_assign(i1p, i1); @RC:代码中定义和初始化整数的位置是以下行的第一行: int i1 = 1; SWIGTYPE_p_int i1p = Message.new_intp(); Message.intp_assign(i1p, i1); SWIGTYPE_p_int 是指向 int 的指针,在第 2 行中创建,并且 int 在第三行中分配给该指针。结构的 i1 元素稍后在代码中通过以下行分配给指针: p_my_struct.setI1(i1p);正如你提到的。 【参考方案1】:

我们也可以使用JavaCPP,这比SWIG好用多了,IMO作为作者。

有了这个配置类:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
import org.bytedeco.javacpp.tools.*;

@Properties(value=@Platform(include="message.h", link="theMessageLibFile"), target="Message")
public class MessageConfig implements InfoMapper 
    public void map(InfoMap infoMap) 
        infoMap.put(new Info("P_S_MESSAGE_STRUCT").valueTypes("S_MESSAGE_STRUCT"));
    

通过执行

$ javac -cp javacpp.jar MessageConfig.java
$ java -jar javacpp.jar MessageConfig

解析器生成这个包装类:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

public class Message extends MessageConfig 
    static  Loader.load(); 

public static class S_MESSAGE_STRUCT extends Pointer 
    static  Loader.load(); 
    public S_MESSAGE_STRUCT()  allocate(); 
    public S_MESSAGE_STRUCT(int size)  allocateArray(size); 
    public S_MESSAGE_STRUCT(Pointer p)  super(p); 
    private native void allocate();
    private native void allocateArray(int size);
    @Override public S_MESSAGE_STRUCT position(int position) 
        return (S_MESSAGE_STRUCT)super.position(position);
    

    public native IntPointer i1(); public native S_MESSAGE_STRUCT i1(IntPointer i1);
    public native @Cast("char*") BytePointer c1(); public native S_MESSAGE_STRUCT c1(BytePointer c1);
    public native int len(); public native S_MESSAGE_STRUCT len(int len);


public static native @Cast("bool") boolean t_func(S_MESSAGE_STRUCT p_s_mystruct);

然后我们可以通过调用来构建原生库:

$ javac -cp javacpp.jar Message.java
$ java -jar javacpp.jar Message

按预期工作,例如:

import org.bytedeco.javacpp.*;

public class Main 
    public static void main(String[] args) 
        Message.S_MESSAGE_STRUCT my_struct = new Message.S_MESSAGE_STRUCT();
        IntPointer i1 = new IntPointer(1).put(1);
        BytePointer c1 = new BytePointer(128);
        my_struct.i1(i1);
        my_struct.c1(c1);
        my_struct.len(c1.capacity());
        Message.t_func(my_struct);
        System.out.println(c1.getString());
    

输出以下内容:

Hello from JavaCPP

【讨论】:

以上是关于带有指针的C结构,如何Swig?的主要内容,如果未能解决你的问题,请参考以下文章

如何为双指针结构参数应用 SWIG 类型映射

SWIG Python - 包装一个需要指向结构的双指针的函数

SWIG (Java):如何将带有回调函数的结构从 Android 应用程序传递给 C++?

Swig:将成员变量(指向值的指针)转换为 python 列表

SWIG:返回原始指针与共享 ptrs 的向量

Swig 中 C 指针的访问器函数