2014-07-04 18 views
8

voglio esporre la seguente funzione C++ a Python utilizzando Boost.Python:funzioni Esporre C++, che puntatore ritorno utilizzando Boost.Python

int* test1() { 
    return new int(42); 
} 

// Now exposing the function with Boost.Python 

BOOST_PYTHON_MODULE(libtest1) 
{ 
    using namespace boost::python; 
    def("test1", test1); 
} 

Quando provo a compilare questa libreria l'errore si verifica a causa di (E ' suppongo) Boost.Python non lo so, come convertire int * in PyObject.

Penso che ciò che deve essere fatto è quello di definire la struttura di conversione, qualcosa di simile:

template<class T> 
struct int_ptr_to_python 
{ 
    static PyObject* convert(int* i_ptr) 
    { 
     return i_ptr; 
    } 
}; 

e passarlo alla dichiarazione BOOST_PYTHON_MODULE:

BOOST_PYTHON_MODULE(libtest1) 
{ 
    using namespace boost::python; 
    def("test1", test1); 
    to_python_converter<int*, int_ptr_to_python<int*> >(); 
} 

Ma anche non funziona . E non riesco a trovare alcuna informazione su come le funzioni, che i puntatori di ritorno dovrebbero essere gestiti.

Qualcuno può aiutare?

risposta

11

In breve, non è possibile esporre direttamente una funzione che restituisce int* con Boost.Python, poiché non esiste un corrispondente tipo corrispondente in Python dato che gli interi sono immutabili.

  • Se l'obiettivo è restituire un numero a Python, restituire il valore int in base al valore. Ciò potrebbe richiedere l'utilizzo di una funzione ausiliaria per adattare le API legacy.
  • Se l'obiettivo è restituire un puntatore a un oggetto allocato con una nuova espressione, l'oggetto deve essere una classe o unione e la funzione deve essere esposta con politiche specifiche per indicare le responsabilità di proprietà.

Piuttosto che presenti la soluzione finale immediatamente, vorrei prendere il tempo per passare da errori di compilatore. Con Boost.Python, talvolta vengono utilizzate asserzioni statiche pre-C++ 11 per fornire istruzioni nei messaggi del compilatore. Sfortunatamente, può essere un po 'difficile trovarli tra i modelli pesanti.

Il codice seguente:

#include <boost/python.hpp> 

int* make_int() { return new int(42); } 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::def("make_int", &make_int); 
} 

produce il seguente output pertinente clang, con i dettagli importanti accentuati in grassetto:

 
.../boost/python/detail/caller.hpp:102:98: error: 
    no member named 'get_pytype' in 'boost::python::detail:: 
    specify_a_return_value_policy_to_wrap_functions_returning<int*>' 
    ...create_result_converter((PyObject*)0, (ResultConverter *)0, 
          (ResultConverter *)0).g... 

Boost.Python ci informa che un boost::python::return_value_policy deve essere specificato per le funzioni che restituiscono int*. Esistono vari modelli di ResultConverterGenerators. Spesso le politiche vengono utilizzate per controllare la proprietà o la semantica della vita dell'oggetto restituito. Poiché la funzione nel codice originale restituisce un nuovo puntatore direttamente a Python, lo boost::python::manage_new_object è appropriato se ci si aspetta che il chiamante si assuma la responsabilità dell'eliminazione dell'oggetto.

Specifica una politica di gestione oggetto non riesce ancora:

#include <boost/python.hpp> 

int* make_int() { return new int(42); } 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::def("make_int", &make_int, 
    python::return_value_policy<python::manage_new_object>()); 
} 

produce il seguente output rilevante:

 
.../boost/python/object/make_instance.hpp:27:9: 
    error: no matching function for call to 'assertion_failed' 
    BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >)); 

In questo caso, Boost.Python ci informa che l'oggetto restituito da una funzione con un ResultConverterGenerator managed_new_object deve essere o class o union. Per un int*, la soluzione più appropriata è restituire il valore int in base al valore durante il passaggio attraverso il livello Boost.Python. Tuttavia, per completezza, di seguito viene illustrato:

  • Utilizzo di una funzione ausiliaria per adattare un'API legacy.
  • Come limitare la creazione di un tipo definito dall'utente con una funzione di fabbrica che restituisce i puntatori.
#include <boost/python.hpp> 

/// Legacy API. 
int* make_int() { return new int(42); } 

/// Auxiliary function that adapts the legacy API to Python. 
int py_make_int() 
{ 
    std::auto_ptr<int> ptr(make_int()); 
    return *ptr; 
} 

/// Auxiliary class that adapts the legacy API to Python. 
class holder 
    : private boost::noncopyable 
{ 
public: 
    holder() 
    : value_(make_int()) 
    {} 

    int get_value() const  { return *value_; } 
    void set_value(int value) { *value_ = value; } 

private: 
    std::auto_ptr<int> value_; 
}; 

/// Factory function for the holder class. 
holder* make_holder() 
{ 
    return new holder(); 
} 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::def("make_int", &py_make_int); 

    python::class_<holder, boost::noncopyable>("Holder", python::no_init) 
    .add_property("value", 
     python::make_function(&holder::get_value), 
     python::make_function(&holder::set_value)) 
    ; 

    python::def("make_holder", &make_holder, 
    python::return_value_policy<python::manage_new_object>()); 
} 

utilizzo interattivo:

>>> import example 
>>> assert(42 == example.make_int()) 
>>> holder = example.Holder() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
RuntimeError: This class cannot be instantiated from Python 
>>> holder = example.make_holder() 
>>> assert(42 == holder.value) 
>>> holder.value *= 2 
>>> assert(84 == holder.value) 
+1

Grazie per la risposta dettagliata! – avli

Problemi correlati