boost - tuplas - metodos de listas en python
Alimentar una lista de Python en una funciĆ³n que toma un vector con Boost Python (1)
Hay algunas soluciones para lograr esto sin tener que modificar las funciones originales.
Para lograr esto con una pequeña cantidad de código repetitivo y transparencia para Python, considere registrar un converter
personalizado . Boost.Python utiliza conversores registrados cuando se encuentra entre los tipos C ++ y Python. Algunos convertidores se crean implícitamente al crear enlaces, como cuando class_
exporta un tipo.
El siguiente ejemplo completo utiliza un tipo de convertidor iterable_converter
que permite el registro de funciones de conversión de un tipo python que admite el protocolo iterable de python . El ejemplo permite conversiones para:
- Colección del tipo incorporado:
std::vector<double>
- Colección bidimensional de cadenas:
std::vector<std::vector<std::String> >
- Colección de tipo de usuario:
std::list<foo>
#include <iostream>
#include <list>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
/// @brief Mockup model.
class foo {};
// Test functions demonstrating capabilities.
void test1(std::vector<double> values)
{
for (auto&& value: values)
std::cout << value << std::endl;
}
void test2(std::vector<std::vector<std::string> > values)
{
for (auto&& inner: values)
for (auto&& value: inner)
std::cout << value << std::endl;
}
void test3(std::list<foo> values)
{
std::cout << values.size() << std::endl;
}
/// @brief Type that allows for registration of conversions from
/// python iterable types.
struct iterable_converter
{
/// @note Registers converter from a python interable type to the
/// provided type.
template <typename Container>
iterable_converter&
from_python()
{
boost::python::converter::registry::push_back(
&iterable_converter::convertible,
&iterable_converter::construct<Container>,
boost::python::type_id<Container>());
// Support chaining.
return *this;
}
/// @brief Check if PyObject is iterable.
static void* convertible(PyObject* object)
{
return PyObject_GetIter(object) ? object : NULL;
}
/// @brief Convert iterable PyObject to C++ container type.
///
/// Container Concept requirements:
///
/// * Container::value_type is CopyConstructable.
/// * Container can be constructed and populated with two iterators.
/// I.e. Container(begin, end)
template <typename Container>
static void construct(
PyObject* object,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
namespace python = boost::python;
// Object is a borrowed reference, so create a handle indicting it is
// borrowed for proper reference counting.
python::handle<> handle(python::borrowed(object));
// Obtain a handle to the memory block that the converter has allocated
// for the C++ type.
typedef python::converter::rvalue_from_python_storage<Container>
storage_type;
void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
typedef python::stl_input_iterator<typename Container::value_type>
iterator;
// Allocate the C++ type into the converter''s memory block, and assign
// its handle to the converter''s convertible variable. The C++
// container is populated by passing the begin and end iterators of
// the python object to the container''s constructor.
new (storage) Container(
iterator(python::object(handle)), // begin
iterator()); // end
data->convertible = storage;
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Register interable conversions.
iterable_converter()
// Build-in type.
.from_python<std::vector<double> >()
// Each dimension needs to be convertable.
.from_python<std::vector<std::string> >()
.from_python<std::vector<std::vector<std::string> > >()
// User type.
.from_python<std::list<foo> >()
;
python::class_<foo>("Foo");
python::def("test1", &test1);
python::def("test2", &test2);
python::def("test3", &test3);
}
Uso interactivo:
>>> import example
>>> example.test1([1, 2, 3])
1
2
3
>>> example.test1((4, 5, 6))
4
5
6
>>> example.test2([
... [''a'', ''b'', ''c''],
... [''d'', ''e'', ''f'']
... ])
a
b
c
d
e
f
>>> example.test3([example.Foo(), example.Foo()])
2
Algunos comentarios sobre este enfoque:
- La función
iterable_converter::convertible
podría cambiar para permitir solo la lista de python, en lugar de permitir cualquier tipo que admita el protocolo iterable. Sin embargo, la extensión puede volverse ligeramente untípica como resultado. - Las conversiones se registran en función de los tipos de C ++. Por lo tanto, el registro solo debe hacerse una vez, ya que se seleccionará la misma conversión registrada en cualquier número de funciones exportadas que acepten el tipo C ++ como un argumento.
- No introduce tipos innecesarios en el espacio de nombres de extensión de
example
. - La meta-programación podría permitir que los tipos multidimensionales registren recursivamente cada tipo de dimensión. Sin embargo, el código de ejemplo ya es lo suficientemente complejo, por lo que no quería agregar un nivel adicional de complejidad.
Los enfoques alternativos incluyen:
- Cree una función personalizada o una función de plantilla que acepte un
boost::python::list
para cada función que acepte unstd::vector
. Este enfoque hace que los enlaces se amplíen en función de la cantidad de funciones que se exportan, en lugar de la cantidad de tipos que necesitan convertirse. Usando el Boost.Python
vector_indexing_suite
. Las clases*_indexing_suite
exportan un tipo que está adaptado para coincidir con algunas semánticas de la lista de Python o diccionarios. Por lo tanto, el código de Python ahora tiene que saber el tipo de contenedor exacto que se debe proporcionar, lo que resulta en una extensión menos pitónica. Por ejemplo, sistd::vector<double>
se exporta comoVecDouble
, el uso resultante de Python sería:v = example.VecDouble() v[:] = [1, 2, 3] example.test1(v)
Sin embargo, lo siguiente no funcionará porque los tipos exactos deben coincidir, ya que al exportar la clase solo se registra una conversión entre
VecDouble
ystd::vector<double>
:example.test1([4, 5, 6])
Si bien este enfoque se adapta a los tipos en lugar de a las funciones, se traduce en una extensión menos pitónica y aumenta el espacio de nombres del
example
con tipos innecesarios.
Tengo una función con la firma:
function(std::vector<double> vector);
Y lo he expuesto, pero no toma en las listas de Python. He revisado las otras respuestas de SO y la mayoría implica cambiar la función para tomar boost :: python :: lists, pero no quiero cambiar la función. Me imagino que puedo usar el vector_indexing_suite para escribir una envoltura simple alrededor de esta función, pero tengo muchas funciones de esta forma y preferiría no escribir una envoltura para cada una. ¿Hay alguna manera de hacer que se produzca automáticamente una asignación de Python list-> std :: vector?