2016-03-12 27 views
2

我有使用包裝結構經由升壓蟒露出C++ myObject的類:升壓蟒返回的元組包含自定義類型

struct myObjectWrapper{ 
    static tuple compute(myObject& o,const Container& x0, const double& t0, Container& x){ 
     double t; 
     int stat = o.evaluate(x0,t0,x,t); 
     return make_tuple(stat,t); 
    } 
} 

BOOST_PYTHON_MODULE(myModule) 
{ 
    // not shown here is code to expose Container class 

    class_<myObject>("MyObject") 
     .def("compute",&myObjectWrapper::compute) 
    ; 
} 

集裝箱當前被定義爲:

typedef std::valarray<double> Container 

並暴露到python。

現在在Python我可以做。

x = Container() 
(status,t) = obj.compute(Container([0.,0.,0.]),0.0,x) 
print status, t, x[0] 

這不是pythonic。我寧願這樣做:

(status,t,x) = obj.compute(Container([0.,0.,0.]),0.0) 
print status, t, x[0]  

我可以在python中編寫額外的包裝,但我寧願避免添加更多的包裝。

下面的代碼編譯簡化版,:

struct myObjectWrapper{ 
    static tuple compute(myObject& o,const Container& x0, const double& t0){ 
     double t; 
     Container x; 
     int stat = o.evaluate(x0,t0,x,t); 
     return make_tuple(stat,t,x); 
    } 
} 

此外,我寧願偷局部變量x的內容和有蟒蛇管理它,而不是複製:

return make_tuple(stat,t,std::move(x)); 

如何我能達到這個目標嗎?

回答

1

總之,在免費商店中分配包裝並使用manage_new_object結果轉換將所有權轉移給Python對象。這將導致Boost.Python在構造Python對象時複製指針,而不是複製指針。有關更多詳細信息,請參閱this答案。

下面是一個輔助功能,將所有權轉讓給Python對象:

/// @brief Transfer ownership to a Python object. If the transfer fails, 
///  then object will be destroyed and an exception is thrown. 
template <typename T> 
boost::python::object transfer_to_python(T* t) 
{ 
    // Transfer ownership to a smart pointer, allowing for proper cleanup 
    // incase Boost.Python throws. 
    std::unique_ptr<T> ptr(t); 

    // Use the manage_new_object generator to transfer ownership to Python. 
    namespace python = boost::python; 
    typename python::manage_new_object::apply<T*>::type converter; 

    // Transfer ownership to the Python handler and release ownership 
    // from C++. 
    python::handle<> handle(converter(*ptr)); 
    ptr.release(); 

    return python::object(handle); 
} 

人們可以按如下方式使用它:

boost::python::tuple myObjectWrapper::compute(
    myObject& o, const Container& x0, const double& t0) 
{ 
    auto x1 = std::make_unique<container>(); 
    double t1 = 0; 
    int stat = self.evaluate(x0, t0, *x1, t1); 
    return boost::python::make_tuple(stat, t1, transfer_to_python(x1.release())); 
} 

下面是基於一個完整的例子原始問題demonstrates使用transfer_to_python輔助功能。

#include <boost/python.hpp> 
#include <cassert> 
#include <memory> // std::unique_ptr 

// Mock legacy API. 
struct container 
{ 
    container() {} 
    container(boost::python::object) {} 

    container(const container&) 
    { 
    // For this example, guarantee copy is not made. 
    assert(false); 
    } 
}; 

struct my_object 
{ 
    int evaluate(container&, double&, container&, double&) { return 42; } 
}; 

/// @brief Transfer ownership to a Python object. If the transfer fails, 
///  then object will be destroyed and an exception is thrown. 
template <typename T> 
boost::python::object transfer_to_python(T* t) 
{ 
    // Transfer ownership to a smart pointer, allowing for proper cleanup 
    // incase Boost.Python throws. 
    std::unique_ptr<T> ptr(t); 

    // Use the manage_new_object generator to transfer ownership to Python. 
    namespace python = boost::python; 
    typename python::manage_new_object::apply<T*>::type converter; 

    // Transfer ownership to the Python handler and release ownership 
    // from C++. 
    python::handle<> handle(converter(*ptr)); 
    ptr.release(); 

    return python::object(handle); 
} 

// API wrapper. 
boost::python::tuple my_object_compute(
    my_object& self, container& x0, double t0) 
{ 
    auto x1 = std::make_unique<container>(); 
    double t1 = 21; 
    int stat = self.evaluate(x0, t0, *x1, t1); 
    return boost::python::make_tuple(stat, t1, transfer_to_python(x1.release())); 
} 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 

    python::class_<container>("Container") 
    .def(python::init<python::object>()) 
    ; 

    python::class_<my_object>("MyObject") 
    .def("compute", &my_object_compute) 
    ; 
} 

互動用法:

>>> import example 
>>> my_object = example.MyObject() 
>>> status, t, x = my_object.compute(example.Container([1, 2, 3]), 4) 
>>> assert(status == 42) 
>>> assert(t == 21) 
>>> assert(isinstance(x, example.Container)) 
+0

謝謝。有用。 –