簡體   English   中英

使用boost-python將python變量設置為C ++對象指針

[英]Set a python variable to a C++ object pointer with boost-python

我想從C ++設置一個Python變量,這樣C ++程序就可以創建一個對象Game* game = new Game(); 為了使Python代碼能夠引用此實例(以及調用函數等)。 我怎樣才能做到這一點?

我覺得我對Python或Boost-Python的工作方式有一些核心的誤解。

main_module.attr("game") = game是在try catch語句中,錯誤(使用PyErr_Fetch)是“為C ++類型找不到to_python(按值)轉換器:class Game”。

例如

class_<Game>("Game")
        .def("add", &Game::add)
;

object main_module = import("__main__");
Game* game = new Game();
main_module.attr("game") = game; //This does not work

來自Python:

import testmodule

testmodule.game.foo(7)

在處理語言綁定時,通常必須在細節上迂腐。 默認情況下,當C ++對象超越語言邊界時,Boost.Python將創建一個副本,因為這是防止懸空引用的最安全的操作過程。 如果不應該創建副本,那么需要明確C ++對象的所有權:

  • 要在保持C ++所有權的同時將對C ++對象的引用傳遞給Python,請使用boost::python::ptr()boost::ref() C ++代碼應該保證C ++對象的生命周期至少和Python對象一樣長。 使用ptr() ,如果指針為null,則生成的Python對象將為None
  • 要將C ++對象的所有權轉移到Python,可以應用manage_new_object ResultConverterGenerator ,允許將所有權轉移到Python。 一旦Python對象的生命周期結束,C ++代碼不應嘗試訪問指針。
  • 對於共享所有權,需要使用支持共享語義的智能指針的HeldType公開該類,例如boost::shared_ptr

一旦創建了Python對象,就需要將其插入到Python命名空間中以便通常可訪問:

  • 在模塊定義中,使用boost::python::scope來獲取當前作用域的句柄。 例如,以下內容將x插入到example模塊中:

     BOOST_PYTHON_MODULE(example) { boost::python::scope().attr("x") = ...; // example.x } 
  • 要插入__main__模塊,可以導入__main__ 例如,以下內容將x插入__main__模塊:

     boost::python::import("__main__").attr("x") = ...; 

下面是一個演示如何從C ++直接構造Python對象,將C ++對象的所有權轉移到Python,以及構造引用C ++對象的Python對象的示例:

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

// Mockup model.
struct spam
{
  spam(int id)
    : id_(id)
  {
    std::cout << "spam(" << id_ << "): "  << this << std::endl;
  }

  ~spam()
  {
    std::cout << "~spam(" << id_ << "): " << this << std::endl;
  }

  // Explicitly disable copying.
  spam(const spam&) = delete;
  spam& operator=(const spam&) = delete;

  int id_;
};

/// @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);
}

namespace {
spam* global_spam;
} // namespace

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  // Expose spam.
  auto py_spam_type = python::class_<spam, boost::noncopyable>(
      "Spam", python::init<int>())
    .def_readonly("id", &spam::id_)
    ;

  // Directly create an instance of Python Spam and insert it into this
  // module's namespace.
  python::scope().attr("spam1") = py_spam_type(1);

  // Construct of an instance of Python Spam from C++ spam, transfering
  // ownership to Python.  The Python Spam instance will be inserted into
  // this module's namespace.
  python::scope().attr("spam2") = transfer_to_python(new spam(2));

  // Construct an instance of Python Spam from C++, but retain ownership of
  // spam in C++.  The Python Spam instance will be inserted into the
  // __main__ scope.
  global_spam = new spam(3);
  python::import("__main__").attr("spam3") = python::ptr(global_spam);
}

互動用法:

>>> import example
spam(1): 0x1884d40
spam(2): 0x1831750
spam(3): 0x183bd00
>>> assert(1 == example.spam1.id)
>>> assert(2 == example.spam2.id)
>>> assert(3 == spam3.id)
~spam(1): 0x1884d40
~spam(2): 0x1831750

在示例用法中,請注意Python在退出時如何不銷毀spam(3) ,因為它未被授予底層對象的所有權。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM