gpt4 book ai didi

python - Boost.Python:如何公开 std::unique_ptr

转载 作者:IT老高 更新时间:2023-10-28 20:29:03 24 4
gpt4 key购买 nike

我对 boost.python 还很陌生,并试图将函数的返回值公开给 python。

函数签名如下所示:

 std::unique_ptr<Message> someFunc(const std::string &str) const;

在python中调用函数时,出现如下错误:

TypeError: No to_python (by-value) converter found for C++ type: std::unique_ptr<Message, std::default_delete<Message> >

我在 python 中的函数调用如下所示:

a = mymodule.MyClass()
a.someFunc("some string here") # error here

我试图公开 std::unique_ptr 但无法让它工作..有人知道如何正确公开指针类吗?谢谢!

编辑:我尝试了以下方法:

class_<std::unique_ptr<Message, std::default_delete<Message>>, bost::noncopyable ("Message", init<>())

;

这个例子编译,但我仍然得到上面提到的错误。另外,我试图公开类 Message 本身

class_<Message>("Message", init<unsigned>())

.def(init<unsigned, unsigned>())
.def("f", &Message::f)
;

最佳答案

简而言之,Boost.Python 不支持移动语义,因此不支持 std::unique_ptr。 Boost.Python 的 news/change log没有迹象表明它已针对 C++11 移动语义进行了更新。此外,此 feature request对于 unique_ptr 的支持已经有一年多没有动过。

不过,Boost.Python 支持通过 std::auto_ptr 将对象的独占所有权转移到 Python 和从 Python 转移。由于 unique_ptr 本质上是 auto_ptr 的更安全版本,因此将使用 unique_ptr 的 API 调整为使用 的 API 应该是相当简单的>auto_ptr:

  • 当 C++ 将所有权转让给 Python 时,C++ 函数必须:
  • 当 Python 将所有权转让给 C++ 时,C++ 函数必须:
    • 通过 auto_ptr 接受实例。 FAQ提到使用 manage_new_object 策略从 C++ 返回的指针将通过 std::auto_ptr 进行管理。
    • 通过 release()
    • auto_ptr 控制权释放到 unique_ptr

给定一个无法更改的 API/库:

/// @brief Mockup Spam class.
struct Spam;

/// @brief Mockup factory for Spam.
struct SpamFactory
{
/// @brief Create Spam instances.
std::unique_ptr<Spam> make(const std::string&);

/// @brief Delete Spam instances.
void consume(std::unique_ptr<Spam>);
};

SpamFactory::make()SpamFactory::consume() 需要通过辅助函数进行包装。

将所有权从 C++ 转移到 Python 的函数通常可以由将创建 Python 函数对象的函数进行包装:

/// @brief Adapter a member function that returns a unique_ptr to
/// a python function object that returns a raw pointer but
/// explicitly passes ownership to Python.
template <typename T,
typename C,
typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...))
{
return boost::python::make_function(
[fn](C& self, Args... args) { return (self.*fn)(args...).release(); },
boost::python::return_value_policy<boost::python::manage_new_object>(),
boost::mpl::vector<T*, C&, Args...>()
);
}

lambda 委托(delegate)给原始函数,releases() 实例的所有权给 Python,调用策略表明 Python 将取得从 lambda 返回的值的所有权。 mpl::vector 描述了 Boost.Python 的调用签名,允许它正确管理语言之间的函数调度。

adapt_unique的结果暴露为SpamFactory.make():

boost::python::class_<SpamFactory>(...)
.def("make", adapt_unique(&SpamFactory::make))
// ...
;

通用适配SpamFactory::consume()比较困难,但是写个简单的辅助函数就够容易了:

/// @brief Wrapper function for SpamFactory::consume_spam().  This
/// is required because Boost.Python will pass a handle to the
/// Spam instance as an auto_ptr that needs to be converted to
/// convert to a unique_ptr.
void SpamFactory_consume(
SpamFactory& self,
std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python.
{
return self.consume(std::unique_ptr<Spam>{ptr.release()});
}

辅助函数委托(delegate)给原函数,将Boost.Python提供的auto_ptr转换为API需要的unique_ptrSpamFactory_consume 辅助函数公开为 SpamFactory.consume():

boost::python::class_<SpamFactory>(...)
// ...
.def("consume", &SpamFactory_consume)
;

这是一个完整的代码示例:

#include <iostream>
#include <memory>
#include <boost/python.hpp>

/// @brief Mockup Spam class.
struct Spam
{
Spam(std::size_t x) : x(x) { std::cout << "Spam()" << std::endl; }
~Spam() { std::cout << "~Spam()" << std::endl; }
Spam(const Spam&) = delete;
Spam& operator=(const Spam&) = delete;
std::size_t x;
};

/// @brief Mockup factor for Spam.
struct SpamFactory
{
/// @brief Create Spam instances.
std::unique_ptr<Spam> make(const std::string& str)
{
return std::unique_ptr<Spam>{new Spam{str.size()}};
}

/// @brief Delete Spam instances.
void consume(std::unique_ptr<Spam>) {}
};

/// @brief Adapter a non-member function that returns a unique_ptr to
/// a python function object that returns a raw pointer but
/// explicitly passes ownership to Python.
template <typename T,
typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (*fn)(Args...))
{
return boost::python::make_function(
[fn](Args... args) { return fn(args...).release(); },
boost::python::return_value_policy<boost::python::manage_new_object>(),
boost::mpl::vector<T*, Args...>()
);
}

/// @brief Adapter a member function that returns a unique_ptr to
/// a python function object that returns a raw pointer but
/// explicitly passes ownership to Python.
template <typename T,
typename C,
typename ...Args>
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...))
{
return boost::python::make_function(
[fn](C& self, Args... args) { return (self.*fn)(args...).release(); },
boost::python::return_value_policy<boost::python::manage_new_object>(),
boost::mpl::vector<T*, C&, Args...>()
);
}

/// @brief Wrapper function for SpamFactory::consume(). This
/// is required because Boost.Python will pass a handle to the
/// Spam instance as an auto_ptr that needs to be converted to
/// convert to a unique_ptr.
void SpamFactory_consume(
SpamFactory& self,
std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python.
{
return self.consume(std::unique_ptr<Spam>{ptr.release()});
}

BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Spam, boost::noncopyable>(
"Spam", python::init<std::size_t>())
.def_readwrite("x", &Spam::x)
;

python::class_<SpamFactory>("SpamFactory", python::init<>())
.def("make", adapt_unique(&SpamFactory::make))
.def("consume", &SpamFactory_consume)
;
}

交互式 Python:

>>> import example
>>> factory = example.SpamFactory()
>>> spam = factory.make("a" * 21)
Spam()
>>> spam.x
21
>>> spam.x *= 2
>>> spam.x
42
>>> factory.consume(spam)
~Spam()
>>> spam.x = 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
None.None(Spam, int)
did not match C++ signature:
None(Spam {lvalue}, unsigned int)

关于python - Boost.Python:如何公开 std::unique_ptr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20581679/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com