1

I'm using Swig to generate a python wrapper for a DLL file. What I do is:

  1. Generate the wrapper and loader file using swig -c++ -python myfile.i
  2. Create a new DLL file, include myfile_wrapper.cxx and compile to _myfile.pyd.
  3. Load the module myfile.py created by Swig in Idle and try to use it.

The interface file looks like:

%module myfile 
/* Make a test with cpointer - needed?? */
%include cpointer.i 
%pointer_functions(MyHandle, my_handle_p);
%pointer_class(int, intp);
%{
#define SWIG_FILE_WITH_INIT
#include "MyFile.h"
}%
%include "MyFile.h"

The function looks like

 typedef struct tagMyHandle
   {
      void* reserved;
   } *MyHandle;

int OpenFile(const char *szPath, MyHandle* pFile); // pFile is an out parameter
int GetNumberOfItems(MyHandle hFile, int *pnNrOfItems); // pnNrOfItems is an out parameter

If I try to use this from Python I have to do like this:

import myfile
handle_p = myfile.new_my_handle_p()
myfile.OpenFile("Path", handle_p)
handle = myfile.my_file_p_value(handle_p)
num_items_p = myfile.new_intp()
myfile.GetNumberOfItems(handle, num_items_p)
num_items = num_items_p.value()

Am I using Swig incorrectly? It feels that it's a very cumbersome way to call the functions that are supposed to be wrapped for Python.

I would like to do something like:

result, handle = OpenFile("path")
result, items = GetNumberIfItems(handle)

I can't change the source code for myfile.h.

I'm looked at input/output parameters, but do I have to define them for each output type? MyFile.h have hundreds of functions with different output types. And it only supports primitive data types, but most types in MyFile.h are not primitive types, but like the struct MyHandle.

I have looked at SWIG function with pointer struct and http://www.swig.org/Doc3.0/Python.html#Python_nn18 as well, but without any good solution.

Update 1 After a lot of help, I've solved most problems, but I still have a few left that I don't understand.

Problem 1:

// For the out parameter, shouldn't be needed?
%typemap(in,numinputs=0) MyHandle* pOutParam (MyHandle h) %{
    $1 = &h;
%}

// For all others
%typemap(in,numinputs=0) MyHandle* (MyHandle h) %{
    $1 = &h;
%}

// For the return type
%typemap(argout) MyHandle* pOutParam (PyObject* o) %{
    o = PyLong_FromVoidPtr(*$1);
    $result = SWIG_Python_AppendOutput($result,o);
%}

%typemap(in) MyHandle %{
    $1 = reinterpret_cast<MyHandle>(PyLong_AsVoidPtr($input));
%}

and the code

int OpenFile(const char *szPath, MyHandle* pOutParam);
int DoSomething(MyHandle* pInParam);

OpenFile works like charm, but DoSomething still tries to return MyHandle instead of taking it as an in parameter, and I don't understand why. %typemap(argout) MyHandle* is only defined for pOutParam.

Problem 2: I don't understand how to make the type map for something like

int GetFileName(char *szPathBuffer, int iLength);

how to create a char buffer and send that in, like I C:

char szBuffer[MAX_PATH]; GetFileName(szBuffer, MAX_PATH);

Maybe something together with cstring_bounded_output or should I do something like

%typemap(in) (char*, int) { 
    $2 = PyString_Size($input); 
    $1 = (char*) malloc($2 * sizeof(char*)); 
} 

but where is it deallocated?

Problem 3: What is the correct mapping for enum values. If I have

typedef enum tagMyEnum {
MyTrue = 1,
MyFalse = 0 } MyEnum;

and the function

int IsCorrect(MyEnum* pOutValue);

@Mark Tolonen: Thanks for all help! I really appreciate it! I've learned so much new things about Swig!

1
  • Yes and no. This is just one way of using SWIG. You can create more advanced typemaps, which allows simpler interfaces, see e.g. how NumPy is wrapped using numpy.i. If you are not familiar with writing advanced typemaps, I can recommend the %extend feature, where the extensions are made in the target language Commented Oct 24, 2017 at 20:40

1 Answer 1

1

Here's an example with a interface similar to what you want to illustrate using typemaps to redefine an interface:

myfile.h

typedef struct tagMyHandle
{
    void* reserved;
} *MyHandle;

int OpenFile(const char *szPath, MyHandle* pFile);
int GetNumberOfItems(MyHandle hFile, int *pnNrOfItems);
// Added this to free the allocated handle.
void CloseFile(MyHandle hFile);

myfile.cpp

A hack implementation of the header...

#include "myfile.h"

int OpenFile(const char *szPath, MyHandle* pFile)
{
    *pFile = new tagMyHandle;
    (*pFile)->reserved = new int(7);
    return 1;
}

int GetNumberOfItems(MyHandle hFile, int *pnNrOfItems)
{
    *pnNrOfItems = *reinterpret_cast<int*>(hFile->reserved) + 5;
    return 1;
}

// mirrors OpenFile to free the allocated handle.
void CloseFile(MyHandle hFile)
{
    delete reinterpret_cast<int*>(hFile->reserved);
    delete hFile;
}

myfile.i

%module myfile 

%{
#include "MyFile.h"
%}

// An input typemap for the an output parameter, called before the C++ function is called.
// It suppresses requiring the parameter from Python, and uses a temporary
// variable to hold the output value.
%typemap(in,numinputs=0) MyHandle* (MyHandle h) %{
    $1 = &h;
%}

// An output argument typemap, called after the C++ function is called.
// It retrieves the output value and converts it to a Python int,
// then appends it to the existing return value.  Python will get a tuple of
// (return_value,handle).
%typemap(argout) MyHandle* (PyObject* o) %{
    o = PyLong_FromVoidPtr(*$1);
    $result = SWIG_Python_AppendOutput($result,o);
%}

// An input typemap that converts a Python int to a MyHandle*.
%typemap(in) MyHandle %{
    $1 = reinterpret_cast<MyHandle>(PyLong_AsVoidPtr($input));
%}

// This applies a pre-defined int* output typemap to all int* parameters.
%apply int *OUTPUT {int *};

%include "MyFile.h"

Output

>>> import myfile
>>> s,h = myfile.OpenFile('path')
>>> s,h
(1, 7706832)
>>> s,v = myfile.GetNumberOfItems(h)
>>> s,v
(1, 12)
>>> myfile.CloseFile(h)
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks! That works like charm! Then I know how to solve my problem!
I have one problem left though! How to deal with something like GetName(char* buffer, int length) to fill the buffer and send back? Do I need a typemap for that as well?
I have one other problem. How to make this work OtherFunc(MyHandle* inParamaterCanBeNull). How to make that differ from when it's an out parameter?
Is it possible to have different typenames for different functions names for example? That could fix the problem.
For buffer/length, you can make typemap a that works with a pair of parameters. For input vs. output parameter, you can make the input parameter const to differentiate, or you can make a typemap that is only applied to a specific named parameter.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.