2015-09-09 9 views
10

Ho difficoltà a generare un wrapper Python attorno a una libreria C++ utilizzando SWIG (versione 3.0.6).Come applicare i typemaps di SWIG OUTPUT per i tipi di classe in Python?

Il mio problema si riferisce all'applicazione del typemap OUTPUT, in particolare nel caso di puntatori/riferimenti a tipi di classi.

Per illustrare, questo è quello che voglio per i tipi standard, e funziona:

// .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] 

Non è necessario passare in "resultLong", ma viene aggiunto al risultato automaticamente. Grande!

Tuttavia, questo non sembra funzionare come mi aspetto quando il tipo di uscita è un po 'di puntatore a un tipo di classe:

// .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 

Il problema sembra essere che SWIG non ha elaborato nella stessa come il tipo semplice. Appare ancora come un parametro "input" nella firma della funzione avvolta.

// 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 *&' 

Qualcuno può dirmi che cosa sto sbagliando o indicarmi la direzione giusta? Qualsiasi aiuto ricevuto con gratitudine! Grazie

+0

Hai provato a usare un doppio puntatore? Ho avuto problemi con i doppi puntatori e il codice generato utilizzando SWIG 2.0.7, ma questo è risolto in 3.X.X –

+0

Grazie per la risposta. Scusa, come apparirebbe nel file interface.i? – SWilliams

+0

Devo dire che non possiedo il C++ e non posso cambiare le firme dei metodi lì. Ho sicuramente bisogno di gestire un metodo prendendo un parametro 'exportedClassType * &'. – SWilliams

risposta

3

Questa domanda è rimasta irrisolta per un po 'di tempo, quindi ho pensato che fosse meglio fornire una soluzione alla domanda. La tipemap OUTPUT si applica solo ai tipi semplici, quindi una soluzione viene data combinando una typemap in e una argout.

Considerare la situazione, in cui abbiamo una classe C++ SampleImpl che implementa un'interfaccia C++ SampleBase, che tecnicamente non è un'interfaccia, poiché implica l'implementazione di un distruttore virtuale. Supponiamo di avere una funzione statica, che restituisce un codice di errore e un'implementazione dell'interfaccia. Quest'ultimo come riferimento a un puntatore, che è la situazione di cui sopra.

intestazione interfaccia:

// 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; 
    }; 
} 

Attuazione intestazione:

// 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; 
    }; 
} 

Attuazione:

// 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"); 
    } 
} 

interfaccia SWIG (usando 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); 
    } 
} 

uso 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

Questo sembra quello che stavo cercando, grazie! – SWilliams

+0

Prego. Ho cambiato tale che un oggetto viene aggiunto alla lista di output solo se la funzione di costruzione o la funzione restituisce 0 per nessun errore. –

2

non è una risposta, ma non abbastanza di reputaiton per un commento :(

Causa è necessario utilizzare il puntatore in C++ e Python non ha puntatori (in modo da non poteva fare niente con il vostro attuale . 'risultato' in Python comunque)

Potrebbe aggiungere wrapper per nascondere i puntatori in .h come è stato suggerito da @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); 
} 

modificare il file .i chiamare nuovo metodo:

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

in Python scrivere qualcosa del genere:

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

e utilizzare 'realResult' per il lavoro futuro.

+0

Grazie per la risposta. Ora capisco cosa intendesse esattamente Jens Munk con i suoi commenti. Ho un problema con l'implementazione di questo. Cosa succede se il metodo da avvolgere è un metodo di classe, ad esempio 'exportedClassType.GetClassType'? Come posso creare un nuovo metodo sovrascritto sulla classe che avrebbe richiesto un 'exportedClasssType_ptr'? – SWilliams

+0

È difficile dare una sovversione senza un esempio reale. Ad esempio è possibile creare la propria classe 'class newClassWithoutPointers: public oldClassWithPointers {}' e modificare tutti i metodi con i dati del puntatore di input. Ma potrebbe funzionare in modo sbagliato se tutti quei metodi chiamano 'new' o' delete' per i puntatori di input. – embrio

+0

Questo non è completamente completo per il mio caso d'uso (vedi i commenti sopra), ma è un chiaro esempio completo che spiega l'approccio e mi fa scollare. Ottieni la grazia, grazie ancora. – SWilliams

0

Penso che sia necessario utilizzare i puntatori. Inoltre non sono sicuro di cosa succederà, quando mescoleremo i typemaps e le dichiarazioni di ritorno. Un file di esempio minimo 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); 

Dopo aver tradotto (io uso sempre CMake), l'utilizzo in pitone sarebbe:

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

Credo che sia meglio utilizzare le dichiarazioni di ritorno al posto di typemaps per tipi di dati scalari . Quando si interfacciano gli array, si consiglia di utilizzare i tipi di caratteri predefiniti di numpy.i.

+0

Spiacente, non vedo come questo si applica ai tipi di classe come richiesto nella domanda. – SWilliams

Problemi correlati