Search code examples
dtypedefswig

Binding with SWIG - typedef'ed types bound incorrectly


I have swig.i file like that:

%module ogr_api

%{
#include "ogr_api.h"
%}

%inline %{
typedef void *OGRSFDriverH;
%}

/* OGRSFDriverRegistrar */

OGRDataSourceH OGROpen( const char *, int, OGRSFDriverH * )`

and I get the following .c wrapper:

...
SWIGEXPORT void * D_OGROpen(char * jarg1, int jarg2, void * jarg3) {
...

That is SWIG translates OGRSFDriverH to just void*. I need to save the type name. How could I do it?

Also I loss const in the first argument, but this is the next question.


Solution

  • Assuming I've understood your question correctly you have a number of opaque "handles" that in C are really typedefs to void*, but in your generated interface you want to enforce stronger type checking. (Note that the default behaviour is correct here in that it permits usage that exactly mirrors C). You want to prevent a handle of one sort accidentally being given to a function that takes a "different" void*, i.e. expose the typedef as a sort of strong typedef.

    You can do that with SWIG without too much trouble. The key thing to remember is that the interface file you give SWIG doesn't always have to exactly match the real C types, so long as the code that gets produced at the end is correct and legal still.

    I put together an example to illustrate this. Given a header file, which is presumably similar in principle to your ogr_api.h:

    #include <stdio.h>
    
    typedef void * HandleType1;
    typedef void * HandleType2;
    
    void foo(HandleType1) {printf("foo\n");}
    void bar(HandleType2) {printf("bar\n");}
    

    You want to only be able to call foo with a HandleType1 and bar with a HandleType2.

    I used the following interface to get such behaviour:

    %module test
    
    %{
    #include "test.h"
    %}
    
    %nodefaultctor HandleType1;
    struct HandleType1 {};
    %nodefaultctor HandleType2;
    struct HandleType2 {};
    
    void foo(HandleType1*);
    void bar(HandleType2*);
    
    %inline {
      // just for testing - presumably you have a creator in your real API
      HandleType1* geth1() { return NULL; }
      HandleType2* geth2() { return NULL; }
    }
    

    The code that gets generated by this is perfectly fine though since it doesn't try to do anything that can't be done with void* and they're both handled as just pointers in the wrapper.

    The %nodefaultctor is needed to prevent SWIG from trying to construct a new handle based on the lies we told it, you'd get a compiler error with out this. (You probably want to suppress the destructor too, or customise it since that will be calling free).

    Which generates a Java interface that only allows the correct handle to be used for each function. I tested this with:

    public class run {
      public static void main(String[] args) {
        System.loadLibrary("test");
        test.foo(test.geth1());
        //test.bar(test.geth1());
        //test.foo(test.geth2());
        test.bar(test.geth2());
      }
    }
    

    It's a bit of a trick, but it works, have a look at the wrapper that gets generated to convince yourself.

    Which compiled and ran as expected. The commented out lines give errors as you'd hope.


    For a D specific solution, where I understand typedef gives a strong typedef (unlike alias which is more like typedef in C) I think you can use something like:

    %module test
    
    typedef void * HandleType1;
    typedef void * HandleType2;
    
    %pragma(d) proxydmodulecode = %{
      typedef void * HandleType1;
      typedef void * HandleType2;
    %}
    
    %typemap(dtype) HandleType1* "HandleType1";
    %typemap(dtype) HandleType2* "HandleType2";
    
    void foo(HandleType1* h1);
    void bar(HandleType2* h2);
    

    To generate the interface you want. The typemaps modify what the types used in the D interface are, the %pragma inserts the typedef into the (proxy) part of the generated interface.