2014-01-24 30 views
0

我寄存器,其将2维NumPy的阵到特征矩阵(称为DenseMatrix)的转换器:自动转换,以提高的shared_ptr在升压蟒

boost::python::converter::registry::push_back(
     &convertible, 
     &construct, 
     boost::python::type_id<DenseMatrix>() 
); 

如果我有一个函数接受DenseMatrix对象和将它导出到Python我可以使用NumPy-Array从Python中调用它。

Boost.Python.ArgumentError: Python argument types in 
    libNSM.takeReference(numpy.ndarray) 
did not match C++ signature: 
    takeReference(Eigen::Matrix<double, -1, -1, 1, -1, -1> {lvalue}) 

我不: 在另一方面,如果我有一个函数接受一个boost :: shared_ptr的(或指针或引用),并将其导出到Python从Python中调用它,当我得到这样一个错误不想写一个从NumPy-Array到boost :: shared_ptr的显式转换器。有没有更好的方法来解决这个问题?

下面是相应的代码,仅仅是明确的:

void takeObject(DenseMatrix m) { 
    // ... 
} 
void takePointer(DenseMatrix* ptr) { 
    // ... 
} 
void takeReference(DenseMatrix* ptr) { 
    // ... 
} 
void takeSharedPointer(boost::shared_ptr<DenseMatrix> ptr) { 
    // ... 
} 
BOOST_PYTHON_MODULE(boostModule) { 
    def("takeObject", &takeObject); 
    def("takePointer", &takePointer); 
    def("takeReference", &takeReference); 
    def("takeSharedPointer", &takeSharedPointer); 
} 

回答

1

如果需要修改DenseMatrix和复印的DenseMatrix是昂贵的,然后有一个numpy.ndarray转换为副本施工的智能指针的转换器管理DenseMatrix(例如boost::shared_ptr)可能是最佳选择。

Boost.Python不支持custom lvalue converters。为了减少悬空引用的可能性并在语言之间提供明确的方向性,Boost.Python将通过const引用将通过转换产生的对象传递给函数。因此,当一个自定义的转换器提供一个参数,则所述功能参数必须由值或由const引用接受参数:

  • takeObject()作品作为DenseMatrix参数将通过复制构造来构成。
  • takeReference(DenseMatrix&)因为DenseMatrix&无法绑定到const DenseMatrix&而失败。

如果已经被注册自定义转换器,一个numpy.ndarray转换为DenseMatrix,则有可能注册一个numpy.ndarray到管理DenseMatrix智能指针时,重用许多代码。

这里是示出了转换Python对象两个定制转换器之间被共享代码一个完整的例子包含一个intxy属性到Spam对象。此外,该示例还显示了如何使用辅助函数通过非const引用或指针传递转换的对象。

#include <iostream> 
#include <boost/make_shared.hpp> 
#include <boost/python.hpp> 
#include <boost/shared_ptr.hpp> 

/// @brief Mockup Spam class. 
struct Spam 
{ 
    int x; 
    int y; 
    Spam() { std::cout << "Spam()" << std::endl; } 
    ~Spam() { std::cout << "~Spam()" << std::endl; } 

    Spam(const Spam& rhs) : x(rhs.x), y(rhs.y) 
    { std::cout << "Spam(const Spam&)" << std::endl; } 
}; 

/// @brief Helper function to ceck if an object has an attributed with a 
///  specific type. 
template <typename T> 
bool hasattr(const boost::python::object& obj, 
      const char* name) 
{ 
    return PyObject_HasAttrString(obj.ptr(), name) && 
     boost::python::extract<T>(obj.attr(name)).check(); 
} 

/// @brief Helper type that provides conversions from a Python object to Spam. 
struct spam_from_python 
{ 
    spam_from_python() 
    { 
    boost::python::converter::registry::push_back(
     &spam_from_python::convertible, 
     &spam_from_python::construct, 
     boost::python::type_id<Spam>()); 
    } 

    /// @brief Check if PyObject contains an x and y int attribute. 
    static void* convertible(PyObject* object) 
    { 
    namespace python = boost::python; 
    python::handle<> handle(python::borrowed(object)); 
    python::object o(handle); 

    // If x and y are not int attributes, then return null. 
    if (!hasattr<int>(o, "x") && hasattr<int>(o, "y")) 
     return NULL; 

    return object; 
    } 

    /// @brief Convert PyObject to Spam. 
    static void construct(
    PyObject* object, 
    boost::python::converter::rvalue_from_python_stage1_data* data) 
    { 
    // Obtain a handle to the memory block that the converter has allocated 
    // for the C++ type. 
    namespace python = boost::python; 
    typedef python::converter::rvalue_from_python_storage<Spam> storage_type; 
    void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; 

    // Allocate the C++ type into the converter's memory block, and assign 
    // its handle to the converter's convertible variable. 
    Spam* spam; 
    data->convertible = spam = new (storage) Spam(); 

    // Initialize spam from an object. 
    initialize_spam(spam, object); 
    } 

    /// @brief Initialize a spam instance based on a python object. 
    static void initialize_spam(Spam* spam, PyObject* object) 
    { 
    namespace python = boost::python; 
    python::handle<> handle(python::borrowed(object)); 
    python::object o(handle); 

    spam->x = python::extract<int>(o.attr("x")); 
    spam->y = python::extract<int>(o.attr("y")); 
    } 
}; 

/// @brief Helper type that provides conversions from a Python object to 
///  boost::shared_ptr<Spam>. 
struct shared_spam_from_python 
{ 
    shared_spam_from_python() 
    { 
    boost::python::converter::registry::push_back(
     &spam_from_python::convertible, 
     &shared_spam_from_python::construct, 
     boost::python::type_id<boost::shared_ptr<Spam> >()); 
    } 

    /// @brief Convert PyObject to boost::shared<Spam>. 
    static void construct(
    PyObject* object, 
    boost::python::converter::rvalue_from_python_stage1_data* data) 
    { 
    // Obtain a handle to the memory block that the converter has allocated 
    // for the C++ type. 
    namespace python = boost::python; 
    typedef python::converter::rvalue_from_python_storage< 
             boost::shared_ptr<Spam> > storage_type; 
    void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; 

    // Allocate the C++ type into the converter's memory block, and assign 
    // its handle to the converter's convertible variable. 
    boost::shared_ptr<Spam>* spam; 
    data->convertible = spam = 
     new (storage) boost::shared_ptr<Spam>(boost::make_shared<Spam>()); 

    // Initialize spam from an object. 
    spam_from_python::initialize_spam(spam->get(), object); 
    } 
}; 

/// @brief Mockup functions acceping Spam in different ways. 
void by_value(Spam spam)   { std::cout << "by_value()" << std::endl; } 
void by_const_ref(const Spam& spam) { std::cout << "by_cref()" << std::endl; } 
void by_ref(Spam& spam)    { std::cout << "by_ref()" << std::endl; } 
void by_ptr(Spam* spam)    { std::cout << "by_ptr()" << std::endl; } 

/// @brief Use auxiliary functions that accept boost::shared_ptr<Spam> and 
///  delegate to functions that have formal parameters of Spam& and 
///  Spam*. 
void by_ref_wrap(boost::shared_ptr<Spam> spam) { return by_ref(*spam); } 
void by_ptr_wrap(boost::shared_ptr<Spam> spam) { return by_ptr(spam.get()); } 

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

    // Enable python to Spam conversion. 
    spam_from_python(); 

    // Enable python to boost::shared_ptr<Spam> conversion. 
    shared_spam_from_python(); 

    // Expose functions that have parameters that can accept a const Spam& 
    // argument. 
    python::def("by_value",  &by_value); 
    python::def("by_const_ref", &by_const_ref); 

    // Expose functions that have parameters that can accept a const 
    // boost::shared_ptr<Spam>& argument. As copies of shared_ptr are cheap, 
    // a copy is used and the managed instance is passed to other functions, 
    // allowing Spam& and Spam* parameters. 
    python::def("by_ptr", &by_ptr_wrap); 
    python::def("by_ref", &by_ref_wrap); 
} 

互动用法:

>>> class Egg: 
...  x = 1 
...  y = 2 
... 
>>> import example 
>>> example.by_value(Egg()) 
Spam() 
Spam(const Spam&) 
by_value() 
~Spam() 
~Spam() 
>>> example.by_const_ref(Egg()) 
Spam() 
by_cref() 
~Spam() 
>>> example.by_ref(Egg()) 
Spam() 
by_ref() 
~Spam() 
>>> example.by_ptr(Egg()) 
Spam() 
by_ptr() 
~Spam() 
+0

如此看来,一个人必须提供一个明确的转换,以一个boost :: shared_ptr的。我真的希望boost :: python中会有一个隐式转换器。 @Tanner:非常感谢您的详细解答! – bennihepp