我真的很想知道是否有可能將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問題,它可能會覆蓋更多的用戶。 –