boost :: python pass reference of python :: list

Realmente me gustaría saber si existe la posibilidad de pasar una referencia de una lista de python a un archivo boost :: python c ++ dll. Lo que quiero lograr es que tengo una lista en python que se puede leer en c ++ en cualquier momento. Digamos que tendría una variable en C ++ que contiene la referencia a la lista.

¿Hay alguna manera de hacer esto? Hasta ahora solo encontré los ctypes en python donde puedo hacer referencias de tipos c primitivos, lo que en este caso no ayuda.

Estoy feliz por cualquier sugerencia o solución (un ejemplo simple sería genial)

Saludos chris

En resumen, Boost.Python mantiene el argumento de Python al pasar la semántica con sus TypeWrappers. Por lo tanto, al pasar una lista en Python a una función C ++ expuesta, se puede crear y mantener una referencia aceptando la lista de Python como un objeto boost::python::list .


La respuesta detallada en realidad tiene un poco más de profundidad. Antes de profundizar en él, permítanme expandir algunas semánticas para evitar confusiones. Con la recolección de basura de Python y la semántica de paso por objeto, la regla general es tratar a los TypeWrappers de Boost.Python como punteros inteligentes.

  • Si la función acepta la lista como un objeto boost::python::list , entonces C ++ ahora tiene una referencia al objeto Python. La vida útil de la lista de Python se prolongará hasta que sea al menos tan larga como el objeto booot::python::list .
  • Si la lista de Python se convierte a un tipo diferente, como std::vector , C ++ ha construido una copia a la lista de Python. Esta copia no tiene asociación con la lista original.

Aquí hay un ejemplo simple de un módulo de C ++ que se puede pasar a una lista de Python, mantener un identificador e imprimir su contenido:

 #include  // std::cout #include  // std::make_pair #include  #include  #include  boost::python::list list; /// @brief Store handle to the list. /// /// @param pylist Python list for which a handle will be maintained. void set(const boost::python::list& pylist) { // As the boost::python::list object is smart-pointer like, this // creates a reference to the python list, rather than creating a // copy of the python list. list = pylist; } // Iterate over the current list, printing all ints. void display() { std::cout << "in display" << std::endl; typedef boost::python::stl_input_iterator iterator_type; BOOST_FOREACH(const iterator_type::value_type& data, std::make_pair(iterator_type(list), // begin iterator_type())) // end { std::cout << data << std::endl; } } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("set", &set); python::def("display", &display); } 

Y su uso:

 >>> import example >>> >>> x = range(2) >>> x [0, 1] >>> example.set(x) >>> example.display() in display 0 1 >>> x[:] = range(7, 10) >>> example.display() in display 7 8 9 

Una complejidad introducida en la pregunta es el deseo de leer la lista de Python en C ++ en cualquier momento . En su caso más complicado, cualquier momento puede ocurrir en cualquier momento, desde dentro de un subproceso de C ++.

Comencemos con lo básico: Global Interpreter Lock (GIL) de Python. En resumen, el GIL es un mutex alrededor del intérprete. Si un subproceso está haciendo algo que afecta el conteo de referencia del objeto administrado por Python, entonces debe haber adquirido el GIL. A veces el recuento de referencias no es muy transparente, considere:

 typedef boost::python::stl_input_iterator iterator_type; iterator_type iterator(list); 

Aunque boost::python::stl_input_iterator acepta la list como una referencia constante, crea una referencia a la lista de Python desde el constructor.

En el ejemplo anterior, como no había subprocesos en C ++, todas las acciones ocurrieron mientras se adquirió la GIL. Sin embargo, si se puede invocar display() en cualquier momento desde C ++, entonces es necesario que se produzca alguna configuración.

Primero, el módulo necesita que Python inicialice la GIL para el enhebrado.

 BOOST_PYTHON_MODULE(example) { PyEval_InitThreads(); // Initialize GIL to support non-python threads. ... } 

Para mayor comodidad, creamos una clase simple para ayudar a administrar la GIL:

 /// @brief RAII class used to lock and unlock the GIL. class gil_lock { public: gil_lock() { state_ = PyGILState_Ensure(); } ~gil_lock() { PyGILState_Release(state_); } private: PyGILState_STATE state_; }; 

Para mostrar las interacciones con un subproceso de C ++, agreguemos funcionalidad al módulo que permitirá que la aplicación programe un retraso para cuando se muestre el contenido de la lista.

 /// @brief Entry point for delayed display thread. /// /// @param Delay in seconds. void display_in_main(unsigned int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds(seconds)); gil_lock lock; // Acquire GIL. display(); // Can safely modify python objects. // GIL released when lock goes out of scope. } /// @brief Schedule the list to be displayed. /// /// @param Delay in seconds. void display_in(unsigned int seconds) { // Start detached thread. boost::thread(&display_in_main, seconds).detach(); } 

Aquí está el ejemplo completo:

 #include  // std::cout #include  // std::make_pair #include  #include  #include  #include  boost::python::list list; /// @brief Store handle to the list. /// /// @param pylist Python list for which a handle will be maintained. void set(const boost::python::list& pylist) { list = pylist; } // Iterate over the current list, printing all ints. void display() { std::cout << "in display" << std::endl; typedef boost::python::stl_input_iterator iterator_type; BOOST_FOREACH(const iterator_type::value_type& data, std::make_pair(iterator_type(list), // begin iterator_type())) // end { std::cout << data << std::endl; } } /// @brief RAII class used to lock and unlock the GIL. class gil_lock { public: gil_lock() { state_ = PyGILState_Ensure(); } ~gil_lock() { PyGILState_Release(state_); } private: PyGILState_STATE state_; }; /// @brief Entry point for delayed display thread. /// /// @param Delay in seconds. void display_in_main(unsigned int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds(seconds)); gil_lock lock; // Acquire GIL. display(); // Can safely modify python objects. // GIL released when lock goes out of scope. } /// @brief Schedule the list to be displayed. /// /// @param Delay in seconds. void display_in(unsigned int seconds) { // Start detached thread. boost::thread(&display_in_main, seconds).detach(); } BOOST_PYTHON_MODULE(example) { PyEval_InitThreads(); // Initialize GIL to support non-python threads. namespace python = boost::python; python::def("set", &set); python::def("display", &display); python::def("display_in", &display_in); } 

Y su uso:

 >>> import example >>> from time import sleep >>> >>> x = range(2) >>> example.set(x) >>> example.display() in display 0 1 >>> example.display_in(3) >>> x[:] = range(7, 10) >>> print "sleeping" sleeping >>> sleep(6) in display 7 8 9 

Mientras que la consola de Python se bloqueó durante 6 segundos en la llamada de sleep(6) , el subproceso de C ++ adquirió el GIL, mostró el contenido de la lista x y lanzó el GIL.