代码之家  ›  专栏  ›  技术社区  ›  Werner

将C++创建的对象附加到python列表并使其由python管理

  •  2
  • Werner  · 技术社区  · 9 年前

    嗯,我已经检查了一段时间,找不到答案。

    我想附加一个暴露给python的对象,比如Foo:

    struct Foo {
      Foo(){ std::cout << "Creating a Foo object" << std::endl;}
      virtual ~Foo(){ std::cout << "Destroying a Foo object" << std::endl;}
    };
    

    我使用Foo继承的对象,有时我想将它们附加到python列表中。为此,我创建了一个FooWrapper,它继承自Foo,并使用复制构造函数

    struct FooWrapper : public Foo {
      FooWrapper(const Foo& foo):Foo(foo){ std::cout << "Creating a copy from foo using FooWrapper" << std::endl;}  
      virtual ~FooWrapper(){ std::cout << "Destroying a FooWrapper object" << std::endl;}
    };
    

    这暴露于python:

    BOOST_PYTHON_MODULE(foo_module)
    {
        using namespace py = boost::python;
        py::class_<FooWrapper>("FooWrapper", py::no_init)…
    }
    

    我有一个方法,将最终的Foo对象作为FooWrapper附加到python列表中,比如:

    void appendFoosToList(py::list &list)
    {
      for ( const Foo* foo : m_foos )
      {
        list.append( FooWrapper( *foo )  );
      }
    }                                                                                                                                 
    

    我怎样才能不创建一个临时对象然后复制到列表中,而是将该对象附加到列表中而不必复制临时对象?

    我看过很多文件( boost_faq , boost_python_wiki ),很多次我 this 运行时错误:

    TypeError:找不到C++类型的to_python(按值)转换器:

    BPL无法从Python对象获取C++值。

    例如,当调用extract(.attr(“ 伦恩 ")()) 要获取对象长度,请省略“()”。

    并且没有找到解决方案。

    我找不到关于这件事的明确文件,所以我来这里作为最后的手段。

    1 回复  |  直到 9 年前
        1
  •  5
  •   Tanner Sansbury    8 年前

    简而言之,在免费存储上分配包装器,并使用 manage_new_object result convert将所有权转移到Python对象。这将导致Boost。Python在构造Python对象时复制指针,而不是复制指针对象。

    C++对象嵌入到Python对象中。这是 HeldType 通过公开类时提供 class_ ,默认为公开的C++类型。通常,在公开函数时,可以使用 CallPolicy 实例。特别是,使用 return_value_policy 带有的CallPolicy 管理新对象 ResultConverterGenerator 允许嵌入类型为指针,Python对象将管理所有权。

    以下函数可用于将对象的所有权转移到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);
    }
    

    示例用法:

    void appendFoosToList(boost::python::list& list)
    {
      for (const Foo* foo : m_foos)
      {
        list.append(transfer_to_python(new FooWrapper(*foo)));
      }
    }
    

    下面是一个完整的示例 demonstrating 这种方法:

    #include <iostream>
    #include <boost/python.hpp>
    
    // Mocks...
    class spam
    {
    public:
      spam() { std::cout << "spam(): " << this << std::endl; }
      spam(const spam&)
      {
        std::cout << "spam(const spam&): " << this << std::endl;
      }
      ~spam() { std::cout << "~spam(): " << this << std::endl; }
    };
    
    /// @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);
    }
    
    void append_to_list(boost::python::list& list)
    {
      list.append(transfer_to_python(new spam()));
    }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::class_<spam>("Spam", python::no_init);
      python::def("append_to_list", &append_to_list);
    }
    

    交互式用法:

    >>> import example
    >>> spams = []
    >>> example.append_to_list(spams)
    spam(): 0x25cbd90
    >>> assert(type(spams[0]) is example.Spam)
    >>> del spams
    ~spam(): 0x25cbd90