我真的很想知道是否有可能将python列表的引用传递给boost :: python C++ dll。我想实现的是我有一个Python中的列表,可以随时用C++读取它。假设你在C++中有一个变量来保存对列表的引用。boost :: python传递参考的python ::列表
有没有办法做到这一点?到目前为止,我只能在python中找到ctypes,在这里我可以引用原始c类型,在这种情况下,这没有帮助。
我很高兴的任何建议或解决方法(一个简单的例子将是巨大的)
问候 克里斯
我真的很想知道是否有可能将python列表的引用传递给boost :: python C++ dll。我想实现的是我有一个Python中的列表,可以随时用C++读取它。假设你在C++中有一个变量来保存对列表的引用。boost :: python传递参考的python ::列表
有没有办法做到这一点?到目前为止,我只能在python中找到ctypes,在这里我可以引用原始c类型,在这种情况下,这没有帮助。
我很高兴的任何建议或解决方法(一个简单的例子将是巨大的)
问候 克里斯
总之,Boost.Python的维护Python的参数传递机制,其TypeWrappers。因此,当将Python中的列表传递给公开的C++函数时,可以通过接受Python列表作为boost::python::list
对象来创建和维护引用。
详细答案实际上有一点深度。在深入研究之前,让我扩展一些语义以避免混淆。使用Python的垃圾收集和传递对象语义,一般的经验法则是将Boost.Python TypeWrappers作为智能指针。
boost::python::list
对象,则C++现在具有对Python对象的引用。 Python列表的生命周期将延长至少与booot::python::list
对象一样长。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。
我想说非常感谢你,我可能犯了一些小错误,我无法看到自己试图获取C++的列表引用。关于多线程,我在另一个关于stackoverflow的问题。我甚至没有想到锁定。所以基本上你比我想象的要多得多(大概是我的问题的1/2),并且让我很开心:D非常感谢你 –
@ChrisS:很高兴帮助。 (另外,如果你用[tag:boost-python]标签标记你的Boost.Python问题,它可能会覆盖更多的用户。 –