11

I am having some trouble generating a Python wrapper around a C++ library using SWIG (version 3.0.6).

My issue relates to applying the OUTPUT typemap, specifically in the case of pointers/references to class types.

To illustrate, this is what I want for standard types, and it works:

// .h
int add(const long arg1,const long arg2,long& resultLong);

// interface.i
%apply long& OUTPUT { long& resultLong };
int add(const long arg1,const long arg2,long& resultLong);

// projectWrapper.py
def add(arg1, arg2):
    return _projectWrapper.add(arg1, arg2)
addTerm = _projectWrapper.add

// usage
>>> result = projectWrapper.add(2, 4)
>>> print result
[0, 6L]

You don't have to pass in "resultLong", but it is appended to the result automatically. Great!

However, this doesn't seem to be working as I expect when the output type is some pointer to a class type:

// .h
int GetClassType(const char* name, exportedClassType*& resultPointer);

class exportedClassType
{...}

// interface.i
%apply exportedClassType*& OUTPUT { exportedClassType*& resultPointer };    
int GetClassType(const char* name, exportedClassType*& resultPointer);

// projectWrapper.py
def GetClassType(name, resultPointer):
    return _projectWrapper.GetClassType(name, resultPointer)
GetClassType = _projectWrapper.GetClassType

The problem seems to be that SWIG has not processed it in the same way as the simple type. It still appears as an "input" parameter in the wrapped function signature.

// attempted usage
>>> classType = projectWrapper.GetClassType("name")
TypeError: GetClassType() takes exactly 2 arguments (1 given)

>>> result = 0
>>> projectWrapper.GetClassType("name", result)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: in method 'GetClassType', argument 2 of type 'exportedClassType *&'

Can somebody please tell me what I'm doing wrong or point me in the right direction? Any help gratefully received! Thanks

5
  • Have you tried using a double pointer? I had issues with double pointers and the generated code using SWIG 2.0.7, but this is solved in 3.X.X Commented Sep 9, 2015 at 17:27
  • Thanks for the reply. Sorry, what would that look like in the interface.i file? Commented Sep 9, 2015 at 17:32
  • I should mention that I don't own the C++ and can't change the method signatures there. I definitely need to handle a method taking a exportedClassType*& parameter. Commented Sep 9, 2015 at 17:37
  • Okay, so no pure C interface. One you could do is make a simple wrapper, which exposes a double pointer instead. Preferably, you could make this wrapper inside your interface file. Commented Sep 9, 2015 at 18:15
  • 1
    A solution to this problem is given here stackoverflow.com/questions/12739331/…. Commented Sep 12, 2015 at 8:41

3 Answers 3

3

This question has appeared as unresolved for quite some time, so I thought that I better provide a solution to the question. The OUTPUT typemap only applies to simple types, so a solution is given by combining an in and an argout typemap.

Consider the situation, where we have a C++ class SampleImpl implementing a C++ interface SampleBase, which is technically not an interface, since it involves the implementation of a virtual destructor. Suppose we have a static function, which returns an error code and an implementation of the interface. The latter as a reference to a pointer, which is the situation above.

Interface header:

// Sample.hpp
#pragma once
namespace Module {
  class SampleBase {
  public:
#ifndef SWIG
    // Hint to the programmer to implement this function
    static int SampleCreate(SampleBase *&obj);
#endif
    virtual ~SampleBase() = default;
  };
}

Implementation header:

// Sample_impl.hpp
#pragma once
#include "Sample.hpp"

namespace Module {
  class SampleImpl : public SampleBase {
  public:
    static int SampleCreate(Module::SampleBase *&obj);

    SampleImpl();
    virtual ~SampleImpl();
  private:
    float a;
  };
}

Implementation:

// Sample_impl.cpp
#include "Sample_impl.hpp"
#include <cstdio>

namespace Module {
  int SampleImpl::SampleCreate(Module::SampleBase*& obj) {
    obj = (SampleBase*) new SampleImpl();
    return 0;
  }
  SampleImpl::SampleImpl() {
    printf("SampleImpl::SampleImpl()\n");
  }

  SampleImpl::~SampleImpl() {
    printf("SampleImpl::~SampleImpl()\n");
  }
}

SWIG interface (using argout typemap)

// example.i
%module example
%{
  #define SWIG_FILE_WITH_INIT
  #include "Sample.hpp"
  #include "Sample_impl.hpp"
%}

%include "typemaps.i"

%typemap(in, numinputs=0) Module::SampleBase *&obj (Module::SampleBase *temp) {
  $1 = &temp;
}

%typemap(argout) Module::SampleBase *& {
  PyObject* temp = NULL;
  if (!PyList_Check($result)) {
    temp = $result;
    $result = PyList_New(1);
    PyList_SetItem($result, 0, temp);

    // Create shadow object (do not use SWIG_POINTER_NEW)
    temp = SWIG_NewPointerObj(SWIG_as_voidptr(*$1),
             $descriptor(Module::SampleBase*),
             SWIG_POINTER_OWN | 0);

    PyList_Append($result, temp);
    Py_DECREF(temp);
  }
}

Usage in Python

import example

// Creating specialization
obj = example.SampleImpl()
del obj

// Creation of object using output typemap
errorCode, obj = example.SampleImpl_SampleCreate()
del obj
Sign up to request clarification or add additional context in comments.

1 Comment

You are welcome. I changed such that an object is only added to the output list if the constructor or function returns 0 for no errors.
3
+50

It is not an answer, just not enough of reputaiton for a comment :(

Cause you need to use pointer in C++ and Python doesn't have pointers (so you could not do anything with your current 'result' in Python anyway).

Could you add wrappers to hide pointers into .h as was suggested by @Jens Munk:

class exportedClassType_ptr {
public:
    exportedClassType* ptr;
    exportedClassType_ptr( exportedClassType& input ) {
        this->ptr = &input;
    }
};

int GetClassType( const char* name, exportedClassType_ptr& resultPointer ) {
    return GetClassType( name, resultPointer.ptr );
}

modify .i file to call new method:

%apply exportedClassType_ptr& OUTPUT { exportedClassType_ptr& resultPointer };    
int GetClassType( const char* name, exportedClassType_ptr& resultPointer );

in Python write something like this:

>>> realResult = projectWrapper.exportedClassType()
>>> result = projectWrapper.exportedClassType_ptr(realResult)
>>> projectWrapper.GetClassType("name", result)

and use 'realResult' for future work.

3 Comments

Thanks for the answer. I see now what exactly Jens Munk meant by his comments. I have one issue with implementing this. What if the method to be wrapped is a class method, say exportedClassType.GetClassType? How can I create a new overwritten method on the class that would take an exportedClasssType_ptr?
It is difficult to give a sugestion without a real example. For example you could create your own derived class class newClassWithoutPointers: public oldClassWithPointers { } and change all methods with input pointer data. But it could work in wrong way if all those methods are calling new or delete for input pointers.
This is not entirely complete for my use case (see comments above), but it is a clear full example that explained the approach and gets me unstuck. You get the bounty, thanks again.
0

I think you need to use pointers. I am also not sure what happens, when mixing out typemaps and return statements. A minimal example file tst.i:

%module tst

%{

  // declaration:
  void add(long *resultLong, const long arg1,const long arg2);
  long mul(const long a, const long b);

  // the code:
  void add(long *resultLong, const long arg1,const long arg2) {
    *resultLong = arg1 + arg2;
  }
  long mul(const long a, const long b) {
    return a*b;
  }

%}

// The wrapper:
%apply (long* OUTPUT) { long* resultLong }; 
void add(long* resultLong, const long arg1,const long arg2);
long mul(const long a, const long b);

After translating (I always use CMake), the usage in python would be:

import tst
x = tst.add(3, 4)  # results in 7L    
y = tst.mul(3, 4)  # results in 12L

I think it is better using return statements instead of typemaps for scalar datatypes. When interfacing arrays, I recommend using the predefined typemaps of numpy.i.

1 Comment

Sorry, I don't see how this applies to class types as asked in the question.

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.