Resolving struct properties for SWIG interface fil

2019-02-21 02:40发布

this is a continuation of question I asked a while back. Create a typemap for a function that returns through arguments

In the previous question the interface file is as follows:

%module test

%{
#include "header.h"
%}

%inline %{
  %immutable;
  struct FieldFetch {
    int status;
    int type;
    char *value;
  };
  %mutable;

  struct FieldFetch gaiaTextReaderFetchField(gaiaTextReaderPtr reader, int field_num) {
    struct FieldFetch result;
    result.status = gaiaTextReaderFetchField(reader, field_num, &result.type, &result.value);
    return result;
  }
%}

%ignore gaiaTextReaderFetchField;
%include "header.h"

I now have to resolve the gaiaTextReaderPtr struct, which resides in structs.h. This struct is at the bottom of the following code though I have included others that are within it as well to give a full picture.

I have underlined the lines with SWIG opaque datatypes being created

/** Virtual Text driver: MAX number of fields */
#define VRTTXT_FIELDS_MAX   65535
/** Virtual Text driver: MAX block size (in bytes) */
#define VRTTXT_BLOCK_MAX 65535

/** Virtual Text driver: TEXT value */
#define VRTTXT_TEXT     1
/** Virtual Text driver: INTEGER value */
#define VRTTXT_INTEGER  2
/** Virtual Text driver: DOUBLE value */
#define VRTTXT_DOUBLE   3
/** Virtual Text driver: NULL value */
#define VRTTXT_NULL 4


/**
 Container for Virtual Text record (line)
 */
    struct vrttxt_line
    {
/* a struct representing a full LINE (aka Record) */
/** current offset (parsing) */
    off_t offset;
//__^________________________________________________________SWIGTYPE_p_off_t
/** line length (in bytes) */
    int len;
/** array of field offsets (where each field starts) */
    int field_offsets[VRTTXT_FIELDS_MAX];
//__^________________________________________________________SWIGTYPE_p_int
/** number of field into the record */
    int num_fields;
/** validity flag */
    int error;
    };

/**
 Container for Virtual Text record (line) offsets 
 */
    struct vrttxt_row
    {
/* a struct storing Row offsets */
/** Line Number */
    int line_no;
/** start offset */
    off_t offset;
//__^________________________________________________________SWIGTYPE_p_off_t
/** record (line) length (in bytes) */
    int len;
/** number of fields into this record */
    int num_fields;
    };

/**
 Container for Virtual Text block of records
 */
    struct vrttxt_row_block
    {
/*
/ for efficiency sake, individual Row offsets 
/ are grouped in reasonably sized blocks
*/
/** array of records [lines] */
    struct vrttxt_row rows[VRTTXT_BLOCK_MAX];
/** number of records into the array */
    int num_rows;
/** min Line Number */
    int min_line_no;
/** max Line Number */
    int max_line_no;
/** pointer to next item [linked list] */
    struct vrttxt_row_block *next;
    };

/** 
 Container for Virtual Text column (field) header
 */
    struct vrttxt_column_header
    {
/* a struct representing a Column (aka Field) header */
/** column name */
    char *name;
/** data type: one of GAIA_NULL_VALUE, GAIA_INT_VALUE, GAIA_DOUBLE_VALUE, GAIA_TEXT_VALUE */
    int type;
    };

/**
 Container for Virtual Text file handling
 */
    typedef struct vrttxt_reader
    {
/* the main TXT-Reader struct */
/** array of columns (fields) */
    struct vrttxt_column_header columns[VRTTXT_FIELDS_MAX];
/** FILE handle */
    FILE *text_file;
//__^________________________________________________________SWIGTYPE_p_FILE
/** handle to ICONV converter object */
    void *toUtf8;       /* the UTF-8 ICONV converter */
//__^________________________________________________________SWIGTYPE_p_void
/** field separator character */
    char field_separator;
/** text separator character (quote) */
    char text_separator;
/** decimal separator */
    char decimal_separator;
/** TRUE if the first line contains column names */
    int first_line_titles;
/** validity flag */
    int error;
/** pointer to first block of records [linked list] */
    struct vrttxt_row_block *first;
/** pointer to last block of records [linked list] */
    struct vrttxt_row_block *last;
/** array of pointers to individual records [lines] */
    struct vrttxt_row **rows;
//__^________________________________________________________SWIGTYPE_p_p_vrttxt_row
/** number of records */
    int num_rows;
/** current Line Number */
    int line_no;
/** max number of columns (fields) */
    int max_fields;
/** current buffer size */
    int current_buf_sz;
/** current buffer offset [parsing] */
    int current_buf_off;
/** I/O buffer */
    char *line_buffer;
/** current field buffer */
    char *field_buffer;
/** array of field offsets [current record] */
    int field_offsets[VRTTXT_FIELDS_MAX];
//__^________________________________________________________SWIGTYPE_p_int
/** array of field lengths [current record] */
    int field_lens[VRTTXT_FIELDS_MAX];
//__^________________________________________________________SWIGTYPE_p_int
/** max field [current record] */
    int max_current_field;
/** current record [line] ready for parsing */
    int current_line_ready;
    } gaiaTextReader;
/**
 Typedef for Virtual Text file handling structure

 \sa gaiaTextReader
 */
    typedef gaiaTextReader *gaiaTextReaderPtr;

Any help would greatly be appreciated for resolution! Regards Hank


Part Two

1: One of the development guys had this to say about void toUtf8:

Hi Hank,

a "void *" pointer simply is a generic opaque memory pointer; it's basically an handle.

in the specific context "void *toUtf8" references the internal struct required by ICONV. the referenced object has to be created by a previous call to gaiaCreateUTF8Converter(), and is expected to be destroyed before or after by invoking gaiaFreeUTF8Converter(); each call to gaiaFreeUTF8Converter() requires this pointer to be passed as an argument.

From your Java/SWIG perspective it simply is a constant value to be passed back and forward exactly as it is. (any attempt to directly change, access or dereferencing this pointer could easily lead to some disaster == system crash)

bye Sandro

2: I have a couple of other structs, these are the last in gg_structs.h, that are having issues using the following.

/** COORDs mem-array */
double *Coords;               /* X,Y [vertices] array */

For the moment I have have just put:

%apply double[] {double *};

This has cleared it up, however I am not really sure if this is correctly. Should I be targeting the arrays individually? Actually I'm pretty sure it's incorrect just looked at the class it created and it shows :

  public void setCoords(double[] value) {
    gg_structsJNI.gaiaLinestring_Coords_set(swigCPtr, this, value);
  }

  public double[] getCoords() {
    return gg_structsJNI.gaiaLinestring_Coords_get(swigCPtr, this);
  }

Shouldn't this have an: int index for it to work correctly? For the double I did this:

%ignore Coords;
%include "gg_structs.h"

%extend gaiaLinestring {
  void setCoords(int index, double value) {
    $self->Coords[index] = value;
  }

  double getCoords(int index) {
    return $self->Coords + index;
  }
}

3: I would be interested in hearing more about providing a proxy that implements AbstactSequentialList. Is this what is refer to as a Dynamic Proxy?

标签: java c swig
1条回答
看我几分像从前
2楼-- · 2019-02-21 02:57

A few of these types are quite easy to map onto something simple and intuitive in Java:

  1. For off_t you can use:

    %apply int { off_t };
    

    to tell SWIG to treat off_t as int in Java. That probably works unless you're expecting off_t to be bigger than an int. For some common typedefs SWIG supplies an appropriate mapping in the library already, I'm slightly surprised off_t isn't one of them.

    (You could also choose to show SWIG a typedef in your interface file instead of using %apply)

  2. For the "simple" arrays (i.e. arrays of things which aren't a struct) doing:

    %include <arrays_java.i> 
    

    is sufficient to get an intuitive Java interface generated, for example adding that causes int field_offsets[VRTTXT_FIELDS_MAX] to be wrapped as public void setField_offsets(int[] value) and a corresponding get. Inside the generated code for the setter there is a test to check the sizes match - this will throw an exception at runtime if they don't, since there's no concept of compile time array size in Java.

  3. This answer discussed wrapping FILE* from Java. In this instance the simplest solution would be to use something like:

    %ignore text_file
    %include "header.h"
    %extend gaiaTextReader {
      void setTextFile(const char *fn) {
        $self->text_file = fopen(fn, "r");
      }
    }
    

    which hides the automatic set/gets for text_file and instead exposes a setter that takes a string and calls fopen.

    You could choose the more complex implementation as in the linked answer still obviously, or you could use %inline instead to provide alternative ways of creating a SWIGTYPE_p_FILE in Java.

  4. With regards to the arrays of structs the simplest solution would be to use %ignore and %extend again, e.g. for columns this is:

    %ignore columns;    
    %include "header.h"
    
    %extend gaiaTextReader {
      struct vrttxt_column_header *getColumn(int i) {
        return $self->columns + i;
      }
    
      void setColumn(struct vrttxt_column_header *c, int i) {
        $self->columns[i] = *c;
      }
    }
    

    This is simpler than writing a typemap (which would involve a lot of JNI calls to do the copy from an Object array into the array of structs).

    A more elegant solution might be to write something that extends AbstractList (using Java code typemaps, depending on how exactly you wanted to do it) in Java and proxies that back via something you've exposed in %extend

  5. The same approach to %extend can be taken with the toUtf8 member:

    %ignore toUtf8;
    %include "header.h"
    
    %extend gaiaTextReader {
      void setToUtf8(const char *from) {
        $self->toUtf8 = iconv_open("tocode", from);
      }
    }
    

    (I'm not sure I've got the usage of this field quite right, but the principle applies regardless).

  6. Your linked list can be traversed "naturally" from Java already although again it would be possible/sensible(?) to provide a proxy that implements AbstractSequentialList.

  7. For the int that's really an enum you could still use proper Java enums to represent it:

    %include <enums.swg>
    %javaconst(1);
    
    enum Type;
    %typemap(jstype) int type "$typemap(jstype,enum Type)"
    %typemap(javain) int type "$typemap(javain,enum Type)"
    
    %include "header.h"
    
    enum Type { TEXT=VRTTXT_TEXT,
                INTEGER=VRTTXT_INTEGER,
                DOUBLE=VRTTXT_DOUBLE,
                NONE=VRTTXT_NULL };
    

    (The order here is important - the faked enum needs to happen after the %include, but the typemaps and the forward declaration need to happen before it).

查看更多
登录 后发表回答