I'm using Swig to generate a python wrapper for a DLL file. What I do is:
- Generate the wrapper and loader file using
swig -c++ -python myfile.i - Create a new DLL file, include myfile_wrapper.cxx and compile to _myfile.pyd.
- 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!
numpy.i. If you are not familiar with writing advanced typemaps, I can recommend the%extendfeature, where the extensions are made in the target language