Как создать карту типа OUTPUT для типа класса?

У меня ранее возникли проблемы с попыткой применить типы типов SWIG OUTPUT к типам классов и задал этот предыдущий вопрос.

Ответы, которые я получил там, были полезны, но все же требуйте, чтобы я попросил SWIG сделать что-то вроде:

%apply exportedClassType& OUTPUT { exportedClassType& result };

Это не работает для меня на SWIG 3.0.6 со следующим сообщением:

Warning 453: Can't apply (exportedClassType &OUTPUT). No typemaps are defined.

От взгляда на документацию:

Имейте в виду, что основной целью файла typemaps.i является поддержка примитивных типов данных. Написание такой функции void foo(Bar *OUTPUT); может не иметь предполагаемого эффекта, так как typemaps.i не определяет правило OUTPUT для Bar.

Не похоже, что это поддерживается. Поэтому я предполагаю, что мой вопрос заключается в том, какую комбинацию typemaps мне нужно определить в файле interface.i, чтобы сгенерированный код оболочки для типов классов шел из этого:

// interface.i
%apply exportedClassType& OUTPUT { exportedClassType& result };
int getClassType(exportedClassType& result);

// interface_wrap.cxx
SWIGINTERN PyObject *_wrap_getClassType(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  exportedClassType *arg1 = 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  PyObject * obj0 = 0 ;
  int result;

  if (!PyArg_ParseTuple(args,(char *)"O:getClassType",&obj0)) SWIG_fail;
  res1 = SWIG_ConvertPtr(obj0, &argp1, SWIGTYPE_p_exportedClassType,  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "getClassType" "', argument " "1"" of type '" "exportedClassType &""'"); 
  }
  if (!argp1) {
    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "getClassType" "', argument " "1"" of type '" "exportedClassType &""'"); 
  }
  arg1 = reinterpret_cast< exportedClassType * >(argp1);
  result = (int)getClassType(*arg1);
  resultobj = SWIG_From_int(static_cast< int >(result));
  return resultobj;
fail:
  return NULL;
}

// wrapper.py
def getClassType(result):
    return _wrapper.getClassType(result)
getClassType = _wrapper.getClassType

к чему-то более подобному этому?

// interface.i
%apply bool& OUTPUT { bool& result };
int getSimpleType(bool& result); 

// interface_wrap.cxx
SWIGINTERN PyObject *_wrap_getSimpleType(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  bool *arg1 = 0 ;
  bool temp1 ;
  int res1 = SWIG_TMPOBJ ;
  int result;

  arg1 = &temp1;
  if (!PyArg_ParseTuple(args,(char *)":getSimpleType")) SWIG_fail;
  result = (int)getSimpleType(*arg1);
  resultobj = SWIG_From_int(static_cast< int >(result));
  if (SWIG_IsTmpObj(res1)) {
    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_bool((*arg1)));
  } else {
    int new_flags = SWIG_IsNewObj(res1) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg1), SWIGTYPE_p_bool, new_flags));
  }
  return resultobj;
fail:
  return NULL;
}

// wrapper.py
def getSimpleType():
    return _wrapper.getSimpleType()
getSimpleType = _wrapper.getSimpleType

Я не понимаю, какие типы ввода/вывода/аргумента необходимы, чтобы SWIG_Python_AppendOutput и т.д. вызывается для моего exportedClassType. Кто-нибудь, пожалуйста, дайте мне несколько указателей? Каламбур не предназначен.

Ответы

Ответ 1

Желаемое поведение может быть достигнуто с помощью комбинации типов in и argout. Я собрал это вместе в макрос class_output_typemaps, чтобы его можно было включить с помощью следующего синтаксиса

%class_output_typemaps(exportedClassType)
%apply (exportedClassType*& ARGOUT_OBJECT) {(exportedClassType *&result)}

Полный пример приведен ниже

example.h

#pragma once
class exportedClassType {
 public:
  exportedClassType();
  ~exportedClassType();
};
int getClassType(exportedClassType*& result);

example.cpp

#include "example.h"
#include <cstdio>

int getClassType(exportedClassType*& result) {
  result = new exportedClassType();
  return 0;
}

exportedClassType::exportedClassType() {printf("ctor\n");}
exportedClassType::~exportedClassType() {printf("dtor\n");}

example.i

%module example
%{
  #define SWIG_FILE_WITH_INIT
  #include "example.h"
%}

%include "typemaps.i"

/* %class_output_typemaps() macro
 *
 * It must be executed for each class CLASS_TYPE, which needs typemaps for output
 */
%define %class_output_typemaps(CLASS_TYPE)

%typemap(in, numinputs=0) (CLASS_TYPE *&ARGOUT_OBJECT) (CLASS_TYPE* temp) {
  $1 = &temp;
}

%typemap(argout) (CLASS_TYPE *&ARGOUT_OBJECT) {
  CLASS_TYPE* newObj;
  *(CLASS_TYPE**)&newObj = *$1;

  PyObject* temp = NULL;
  if (!PyList_Check($result)) {
    temp = $result;
    $result = PyList_New(1);
    PyList_SetItem($result, 0, temp);
  }

  temp = SWIG_NewPointerObj(SWIG_as_voidptr(newObj),
                $descriptor(CLASS_TYPE*),
                SWIG_POINTER_OWN | 0);

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

%enddef    /* %class_output_typemaps() */

%class_output_typemaps(exportedClassType)

%apply (exportedClassType*& ARGOUT_OBJECT) {(exportedClassType *&result)}

%include "example.h"

setup.py

#!/usr/bin/env python
from distutils.core import setup, Extension
import numpy
import os

setup(name='test',
      version='1.0',
      ext_modules =[Extension('_example',
                              ['example.i', 'example.cpp'],
                              swig_opts=['-c++'],
                              include_dirs = ['.'])])

После создания с помощью python setup.py build_ext --inplace вы можете создавать объекты из Python с помощью

import example
myExportedClassType = example.getClassType()[1]