2013-07-15 105 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問題,它可能會覆蓋更多的用戶。 –