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)
Grazie per la risposta dettagliata! – avli