2013-07-15 64 views
2

我真的很想知道是否有可能将python列表的引用传递给boost :: python C++ dll。我想实现的是我有一个Python中的列表,可以随时用C++读取它。假设你在C++中有一个变量来保存对列表的引用。boost :: python传递参考的python ::列表

有没有办法做到这一点?到目前为止,我只能在python中找到ctypes,在这里我可以引用原始c类型,在这种情况下,这没有帮助。

我很高兴的任何建议或解决方法(一个简单的例子将是巨大的)

问候 克里斯

回答

6

总之,Boost.Python的维护Python的参数传递机制,其TypeWrappers。因此,当将Python中的列表传递给公开的C++函数时,可以通过接受Python列表作为boost::python::list对象来创建和维护引用。


详细答案实际上有一点深度。在深入研究之前,让我扩展一些语义以避免混淆。使用Python的垃圾收集和传递对象语义,一般的经验法则是将Boost.Python TypeWrappers作为智能指针。

  • 如果该函数接受该列表作为boost::python::list对象,则C++现在具有对Python对象的引用。 Python列表的生命周期将延长至少与booot::python::list对象一样长。
  • 如果Python列表被转换为不同的类型,如std::vector,那么C++已经构建了Python列表的一个副本。此副本与原始列表无关。

这里是一个可以传递一个Python列表中的C++模块的一个简单的例子,保持它的句柄,并打印其内容:

#include <iostream> // std::cout 
#include <utility> // std::make_pair 
#include <boost/foreach.hpp> 
#include <boost/python.hpp> 
#include <boost/python/stl_iterator.hpp> 

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<int> 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); 
} 

及其用法:

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

问题中引入的一个复杂性是希望在任何时候读取C++中的Python列表。在其最复杂的情​​况下,任何时间都可能发生在任何时间点,从C++线程内。

让我们从基础开始:Python的Global Interpreter Lock(GIL)。总之,GIL是解释器周围的一个互斥体。如果一个线程正在做任何影响Python管理对象的引用计数的事情,那么它需要获取GIL。有时候引用计数也不是很透明的,可以考虑:

typedef boost::python::stl_input_iterator<int> iterator_type; 
iterator_type iterator(list); 

虽然boost::python::stl_input_iterator接受list作为一个恒定的参考,它在构造函数中创建的Python列表的引用。

在前面的例子中,因为没有C++线程,所有的操作都是在获取GIL时发生的。但是,如果在C++中随时可以调用display(),则需要进行一些设置。

首先,模块需要Python为线程初始化GIL。

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

为方便起见,让我们创建一个简单的类来帮助管理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_; 
}; 

,以显示与一个C++线程交互,允许添加功能模块,这将允许应用程序安排延迟列表内容的显示时间。

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

下面是完整的例子:

#include <iostream> // std::cout 
#include <utility> // std::make_pair 
#include <boost/foreach.hpp> 
#include <boost/python.hpp> 
#include <boost/python/stl_iterator.hpp> 
#include <boost/thread.hpp> 

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<int> 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); 
} 

及其使用:

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

虽然受阻6秒在sleep(6)呼叫Python的控制台,C++的线程获得的GIL ,显示列表x的内容,并发布了GIL。

+0

我想说非常感谢你,我可能犯了一些小错误,我无法看到自己试图获取C++的列表引用。关于多线程,我在另一个关于stackoverflow的问题。我甚至没有想到锁定。所以基本上你比我想象的要多得多(大概是我的问题的1/2),并且让我很开心:D非常感谢你 –

+0

@ChrisS:很高兴帮助。 (另外,如果你用[tag:boost-python]标签标记你的Boost.Python问题,它可能会覆盖更多的用户。 –