简体   繁体   中英

C structure with pointers, how to Swig?

I am trying to use Swig to generate wrappers for some in-house C-code so I can reuse it for a new Android java project and am having problems. I am new to both Java and Swig so please be gentle with me in terms of the technical content of any replies.

I am trying to wrap a C-structure called S_MESSAGE_STRUCT which contains pointers in such a way that its elements can be accessed (set up, written, read etc) from java and also by legacy C code. My C header file defining the struct is as follows, the code is exemplary and simplified from my real code (which has the same problems) :

#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_ */

My C file contains a single function which tests the int* i1 element of the S_MESSAGE_STRUCT and then writes to the c1 and i1 elements as follows :

#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 am using a .i file as follows :

/* 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);

My java source function is as follows (edited from a default 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);
}

Everything compiles and builds OK and there are no errors from the swig invocation of :

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

but I know that I am doing something wrong because I keep getting flakey problems after I call the t_func() function from java. The problems are various and include the program hanging after the call to t_func() completes but sometimes the program runs and displays an output OK. When output is displayed the contribution due to c1 is null (it just gives output "15" with no signs of "Hello from swig".

I have determined that the program flakiness stems from the writes to the pointer elements i1 and c1 in the P_MYSTRUCT struct, because there are no problems either without those elements or without the writes (but leaving the c1,i1 elements in the struct).

I would be very grateful for help on how to achieve my goal of being able to use pointer elements of C structs and for any ideas of what I am doing wrong in my Message.i file ? I am using SWIG Version 3.0.2. Thanks,

Mike

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!Edit:

I discovered that if I change the following line in message.i

%apply signed char * INOUT {char*};

to

%apply byte * INOUT {char*};

my Swig invocation gives the following error :

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

However the generated code compiles and runs OK with the proviso that I need to change my java application code to use a String instead of a byte[] for its version of c1.

My new java code is :

@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);
}

Does this shed any light on what I may be doing wrong ?

Any ideas gratefully received,

Mike

We could also use JavaCPP , which is a lot easier to use than SWIG, IMO as the author.

With this config class:

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"));
    }
}

And by executing

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

The parser generates this wrapper class:

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);
}

We can then build the native library by calling:

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

Which works as expected, for example:

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());
    }
}

Outputs the following:

Hello from JavaCPP

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM