How to apply SWIG OUTPUT typemaps for class types in Python?

后端 未结 3 816
青春惊慌失措
青春惊慌失措 2020-12-17 19:52

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 i

相关标签:
3条回答
  • 2020-12-17 20:07

    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.

    0 讨论(0)
  • 2020-12-17 20:09

    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
    
    0 讨论(0)
  • 2020-12-17 20:13

    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.

    0 讨论(0)
提交回复
热议问题