智能指针投射在 Boost::P ython



我尝试使用Boost::P ython为现有库创建Python绑定。该库使用自定义智能指针(在以下示例中称为 SmartPointer)。还有两个类,BaseDerived(继承自 Base )。

当我想调用一个期望以SmartPointer<Base>作为参数的SmartPointer<Derived>的函数时,就会出现问题。

在这种情况下,有没有办法告诉 Boost::P ython 尝试将SmartPointer<Base>"降级"到SmartPointer<Derived>?我知道这种"向下"可能会失败,但它会增加很多便利。

下面是一个最小的代码示例:(根据您的系统,您可以使用g++ code.cpp -shared -o example.so -fPIC -I/usr/include/python3.2mu -lboost_python3 -lpython3.2mu编译它)

#include <boost/python.hpp>
#include <iostream>
// ******** code to wrap ********
template <typename T>
class SmartPointer
{
public:
    explicit SmartPointer(T* p) : ptr(p) {}
    template <typename Y>
    explicit SmartPointer(Y* p) : ptr(static_cast<T*>(p)) {}
    template <typename Y>
    SmartPointer(SmartPointer<Y> const& src) : ptr(src.get()) {}
    T& operator*(void) const { return *ptr; }
    T* operator->(void) const { return ptr; }
    T* get(void) const { return ptr; }
protected:
    T* ptr;
};
class Base
{
public:
    virtual ~Base() {}
    virtual void say() const { std::cout << "Base" << std::endl; }
};
class Derived : public Base
{
public:
    virtual void say() const { std::cout << "Derived" << std::endl; }
    static SmartPointer<Base> create_base() { return SmartPointer<Base>(new Derived()); }
};
// ******** test functions ********
void test_basedirect(Base const& d) {
    d.say();
}
void test_basepointer(SmartPointer<Base> const& p) {
    p->say();
}
void test_deriveddirect(Derived const& d) {
    d.say();
}
void test_derivedpointer(SmartPointer<Derived> const& p) {
    p->say();
}
// ******** Boost::Python wrapping code ********
template <typename T>
T* get_pointer(SmartPointer<T> const& p) {
    return p.get();
}
namespace boost { namespace python {
    template <typename T>
    struct pointee<SmartPointer<T> > {
        typedef T type;
    };
}}
BOOST_PYTHON_MODULE(example) {
    using namespace boost::python;
    class_<Base, SmartPointer<Base>, boost::noncopyable>("Base", init<>())
        .def("say", &Base::say)
    ;
    class_<Derived, SmartPointer<Derived>, bases<Base>, boost::noncopyable>("Derived", init<>())
        .def("say", &Derived::say)
        .def("create_base", &Derived::create_base)
    ;
    def("test_basedirect", test_basedirect);
    def("test_basepointer", test_basepointer);
    def("test_deriveddirect", test_deriveddirect);
    def("test_derivedpointer", test_derivedpointer);
    implicitly_convertible<SmartPointer<Derived>, SmartPointer<Base> >();
}

以及一个 Python 会话,显示对函数的失败调用,期望SmartPointer<Derived>作为其参数:

>>> from example import *
>>> d = Derived.create_base()
>>> test_basedirect(d)
Derived 
>>> test_basepointer(d)
Derived 
>>> test_deriveddirect(d)
Derived 
>>> test_derivedpointer(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    example.test_derivedpointer(Derived)
did not match C++ signature:
    test_derivedpointer(SmartPointer<Derived>)
>>> 

假设不能更改Derived::create_base()以返回SmartPointer<Derived>,这将允许在其他地方处理隐式转换,那么通过显式注册SmartPointer<Derived>转换器仍然可以进行转换。 当一个Python对象被传递给C++时,Boost.Python将在其注册表中查找任何可以构造必要的C++对象的转换器。 在这种情况下,需要间接寻址,因为转换发生在公开C++类的HeldType上。 Derived HeldType的转换步骤如下:

  1. 如果 Python 对象同时包含DerivedSmartPointer<Base> C++对象,请继续转换。
  2. 从 Python 对象中提取SmartPointer<Base>
  3. 调用从SmartPointer<Base>构造SmartPointer<Derived>的自定义函数。

下面是一个基于原始代码的完整示例:

#include <iostream>
#include <boost/python.hpp>
#include <boost/static_assert.hpp>
template <typename T>
class SmartPointer
{
public:
  explicit SmartPointer(T* p) : ptr(p) {}
  template <typename Y>
  explicit SmartPointer(Y* p) : ptr(static_cast<T*>(p)) {}
  template <typename Y>
  SmartPointer(SmartPointer<Y> const& src) : ptr(src.get()) {}
  T& operator*(void) const { return *ptr; }
  T* operator->(void) const { return ptr; }
  T* get(void) const { return ptr; }
protected:
  T* ptr;
};
class Base
{
public:
  virtual ~Base() {}
  virtual void say() const { std::cout << "Base" << std::endl; }
};
class Derived
  : public Base
{
public:
  virtual void say() const
  {
    std::cout << "Derived: " << this << std::endl;
  }
  static SmartPointer<Base> create_base()
  {
    return SmartPointer<Base>(new Derived());
  }
};
class OtherDerived
  : public Base
{
public:
  virtual void say() const
  {
    std::cout << "OtherDerived: " << this << std::endl;
  }
  static SmartPointer<Base> create_base()
  {
    return SmartPointer<Base>(new OtherDerived());
  }
};
void test_basedirect(Base const& d)                      { d.say();  }
void test_basepointer(SmartPointer<Base> const& p)       { p->say(); }
void test_deriveddirect(Derived const& d)                { d.say();  }
void test_derivedpointer(SmartPointer<Derived> const& p) { p->say(); }
// Boost.Python wrapping code.
template <typename T>
T* get_pointer(SmartPointer<T> const& p)
{
  return p.get();
}
namespace boost {
namespace python {
template <typename T>
struct pointee<SmartPointer<T> >
{
  typedef T type;
};
} // namespace python
} // namespace boost
namespace detail {
// @brief Construct Source from Target.
template <typename Source,
          typename Target>
Source construct_helper(Target& target)
{
  // Lookup the construct function via ADL.  The second argument is
  // used to:
  // - Encode the type to allow for template's to deduce the desired
  //   return type without explicitly requiring all construct functions
  //   to be a template.
  // - Disambiguate ADL when a matching convert function is declared
  //   in both Source and Target's enclosing namespace.  It should
  //   prefer Target's enclosing namespace.
  return construct(target, static_cast<boost::type<Source>*>(NULL));
}
} // namespace detail
/// @brief Enable implicit conversions between Source and Target types
///        within Boost.Python.
///
///        The conversion of Source to Target should be valid with
///        `Target t(s);` where `s` is of type `Source`.
///
///        The conversion of Target to Source will use a helper `construct`
///        function that is expected to be looked up via ADL.
///
///        `Source construct(Target&, boost::type<Source>*);`
template <typename Source,
          typename Target>
struct two_way_converter
{
  two_way_converter()
  {
    // Enable implicit source to target conversion.
    boost::python::implicitly_convertible<Source, Target>();
    // Enable target to source conversion, that will use the convert
    // helper.
    boost::python::converter::registry::push_back(
      &two_way_converter::convertible,
      &two_way_converter::construct,
      boost::python::type_id<Source>()
    );
  }
  /// @brief Check if PyObject contains the Source pointee type.
  static void* convertible(PyObject* object)
  {
    // The object is convertible from Target to Source, if:
    // - object contains Target.
    // - object contains Source's pointee.  The pointee type must be
    //   used, as this is the converter for Source.  Extracting Source
    //   would cause Boost.Python to invoke this function, resulting
    //   infinite recursion.
    typedef typename boost::python::pointee<Source>::type pointee;
    return boost::python::extract<Target>(object).check() &&
           boost::python::extract<pointee>(object).check()
        ? object
        : NULL;
  }
  /// @brief Convert PyObject to Source type.
  static void construct(
    PyObject* object,
    boost::python::converter::rvalue_from_python_stage1_data* data)
  {
    namespace python = boost::python;
    // Obtain a handle to the memory block that the converter has allocated
    // for the C++ type.
    typedef python::converter::rvalue_from_python_storage<Source>
                                                            storage_type;
    void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
    // Extract the target.
    Target target = boost::python::extract<Target>(object);
    // Allocate the C++ type into the converter's memory block, and assign
    // its handle to the converter's convertible variable.  The C++ type
    // will be copy constructed from the return of construct function.
    data->convertible = new (storage) Source(
      detail::construct_helper<Source>(target));
  }
};
/// @brief Construct SmartPointer<Derived> from a SmartPointer<Base>.
template <typename Derived>
Derived construct(const SmartPointer<Base>& base, boost::type<Derived>*)
{
  // Assumable, this would need to do more for a true smart pointer.
  // Otherwise, two unrelated sets of smart pointers are managing the
  // same instance.
  return Derived(base.get());
}
BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  // Expose Base.
  python::class_<Base, SmartPointer<Base>, boost::noncopyable>(
      "Base", python::init<>())
    .def("say", &Base::say)
    ;
  // Expose Derived.
  python::class_<Derived, SmartPointer<Derived>, 
       python::bases<Base>, boost::noncopyable>(
         "Derived", python::init<>())
    .def("say", &Derived::say)
    .def("create_base", &Derived::create_base)
    .staticmethod("create_base");
    ;
  // Expose OtherDerived.
  python::class_<OtherDerived, SmartPointer<OtherDerived>, 
      python::bases<Base>, boost::noncopyable>(
        "OtherDerived", python::init<>())
    .def("say", &OtherDerived::say)
    .def("create_base", &OtherDerived::create_base)
    .staticmethod("create_base");
    ;
  // Expose Test functions.
  python::def("test_basedirect",          &test_basedirect);
  python::def("test_basepointer",         &test_basepointer);
  python::def("test_deriveddirect",       &test_deriveddirect);
  python::def("test_derivedpointer",      &test_derivedpointer);
  // Enable conversions between the types.
  two_way_converter<SmartPointer<Derived>,      SmartPointer<Base> >();
  two_way_converter<SmartPointer<OtherDerived>, SmartPointer<Base> >();
}

及其用法:

>>> from example import *
>>> d = Derived.create_base()
>>> print d
<example.Derived object at 0xb7f34b1c>
>>> test_basedirect(d)
Derived: 0x8f4de18
>>> test_basepointer(d)
Derived: 0x8f4de18
>>> test_deriveddirect(d)
Derived: 0x8f4de18
>>> test_derivedpointer(d)
Derived: 0x8f4de18
>>> 
>>> o = OtherDerived.create_base()
>>> print o
<example.OtherDerived object at 0xb7f34b54>
>>> test_basedirect(o)
OtherDerived: 0x8ef6dd0
>>> test_basepointer(o)
OtherDerived: 0x8ef6dd0
>>> test_derivedpointer(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    example.test_derivedpointer(OtherDerived)
did not match C++ signature:
    test_derivedpointer(SmartPointer<Derived>)

关于实现的一些说明/评论:

  • two_way_converter假定类型参数HeldType s。 因此,boost::python::pointee<T>::type必须对HeldType有效。
  • two_way_converter允许用户启用自己的自定义策略,通过在Target的封闭命名空间中声明一个Source construct(Target, boost::type<Source>*)函数,从Target构造一个Source。 第二个参数的值没有意义,因为它将始终NULL
  • two_way_converter::convertible()的标准需要足够彻底,Source construct(Target)不会失败。

此外,test_deriveddirect()工作是因为 Boost.Python 在从C++对象创建 Python 对象时执行内省。 当类公开时,Boost.Python 会用类型信息构造一个图。 当一个C++对象传递给 Python 时,Boost.Python 将横向图形,直到根据C++对象的动态类型找到合适的 Python 类型。 找到后,Python 对象将被分配并保存C++对象或其HeldType

在示例代码中,Boost.Python 知道BaseSmartPointer<Base>持有,Derived 派生自Base。 因此,Derived可能由SmartPointer<Base>持有。 当SmartPointer<Base>传递给 Python 时,Boost.Python 通过 get_pointer() 函数获取指向C++对象的指针。 静态类型 Base 用于在图形中查找节点,然后尝试识别C++对象的动态类型时发生遍历。 这导致 Boost.Python 在SmartPointer<Base>指向动态类型为 Base 的对象时创建一个example.Base对象,并在SmartPointer<Base>指向动态类型为 Derived 的对象时创建一个example.Derived对象。

>>> d = Derived.create_base()
>>> print d
<example.Derived object at 0xb7f34b1c>

正如Boost.Python所知d包含一个C++ Derived对象,因此调用test_deriveddirect(d)是有效的。

相关内容

  • 没有找到相关文章

最新更新