首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将类型对象(类,而不是实例)从python传递到c++

将类型对象(类,而不是实例)从python传递到c++
EN

Stack Overflow用户
提问于 2014-04-30 16:11:15
回答 1查看 1.6K关注 0票数 2

我希望有一个boost::python-wrapped c++函数,它能够接收类型(而不是实例),一个boost::python-wrapped c++类。我可以声明使用object的包装函数,但是如何提取类型呢?我尝试过这样做,但是类型对象似乎不是extract-able:

代码语言:javascript
复制
#include<boost/python.hpp>
namespace py=boost::python;

struct A {};
struct B: public A {};

int func(py::object klass) {
    py::extract<std::type_info> T(klass);
    if(!T.check()) throw std::runtime_error("Unable to extract std::type_info");
    if(T()==typeid(A)) return 0;
    if(T()==typeid(B)) return 1;
    return -1;
}

BOOST_PYTHON_MODULE(deadbeef)
{
   py::def("func",func);
   py::class_<A>("A");
   py::class_<B,py::bases<A>>("B");
}

代码语言:javascript
复制
clang++ -lboost_python -fPIC `pkg-config python --cflags` a.cc -std=c++11 -shared -o deadbeef.so

我跑

代码语言:javascript
复制
PYTHONPATH=. python
>>> import deadbeef
>>> deadbeef.func(deadbeef.A)  ## I want this to return 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: Unable to extract std::type_info

谢谢你的建议。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-05-02 20:17:33

要传递Python对象,需要创建C++类型并注册自定义转换器。由于Python对象是python对象,因此创建从boost::python::object派生的类型是合适的:

代码语言:javascript
复制
/// @brief boost::python::object that refers to a type.
struct type_object: 
  public boost::python::object
{
  /// @brief If the object is a type, then refer to it.  Otherwise,
  ///        refer to the instance's type.
  explicit
  type_object(boost::python::object object):
    boost::python::object(get_type(object))
  {}

private:

  /// @brief Get a type object from the given borrowed PyObject.
  static boost::python::object get_type(boost::python::object object)
  {
    return PyType_Check(object.ptr())
      ? object 
      : object.attr("__class__");
  }
};

// ... register custom converter for type_object.

然而,示例代码带来了另一个问题。不能直接对Python对象和C++类型进行比较。此外,Python对象与C++类型没有直接关联。要执行比较,需要比较Python类型的对象。

Boost.Python使用内部注册表将C++类型标识(以boost::python::type_info的形式)与Python类对象相关联。这种关联是单向的,因为它只能查找Python类对象.让我们扩展type_object类以允许提供用于检查C++类型的辅助函数:

代码语言:javascript
复制
/// @brief boost::python::object that refers to a type.
struct type_object: 
  public boost::python::object
{
  ...

  /// @brief Type identity check.  Returns true if this is the object returned
  ///        returned from type() when passed an instance of an object created
  ///        from a C++ object with type T.
  template <typename T>
  bool is() const
  {
    // Perform an identity check that registartion for type T and type_object
    // are the same Python type object.
    return get_class_object<T>() == static_cast<void*>(ptr());
  }

  /// @brief Type identity check.  Returns true if this is the object is a
  ///        subclass of the type returned returned from type() when passed
  ///        an instance of an object created from a C++ object with type T.
  template <typename T>
  bool is_subclass() const
  {
    return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(ptr()),
                            get_class_object<T>());
  }

private:

  ...

  /// @brief Get the Python class object for C++ type T.
  template <typename T>
  static PyTypeObject* get_class_object()
  {
    namespace python = boost::python;
    // Locate registration based on the C++ type.
    const python::converter::registration* registration =
          python::converter::registry::query(python::type_id<T>());

    // If registration exists, then return the class object.  Otherwise,
    // return NULL.
    return (registration) ? registration->get_class_object()
                          : NULL;
  }
};

现在,如果typetype_object的一个实例,您可以检查:

  • 如果type是与C++、Spam类型和type.is<Spam>()相关联的Python类型。
  • 如果type是C++类型的子类,则Spam类型与type.is_subclass<Spam>()相关联。

下面是一个完整的示例,它基于演示接收类型对象到函数的原始代码,检查类型标识和子类:

代码语言:javascript
复制
#include <boost/python.hpp>

/// @brief boost::python::object that refers to a type.
struct type_object: 
  public boost::python::object
{
  /// @brief If the object is a type, then refer to it.  Otherwise,
  ///        refer to the instance's type.
  explicit
  type_object(boost::python::object object):
    boost::python::object(get_type(object))
  {}

  /// @brief Type identity check.  Returns true if this is the object returned
  ///        returned from type() when passed an instance of an object created
  ///        from a C++ object with type T.
  template <typename T>
  bool is() const
  {
    // Perform an identity check that registartion for type T and type_object
    // are the same Python type object.
    return get_class_object<T>() == static_cast<void*>(ptr());
  }

  /// @brief Type identity check.  Returns true if this is the object is a
  ///        subclass of the type returned returned from type() when passed
  ///        an instance of an object created from a C++ object with type T.
  template <typename T>
  bool is_subclass() const
  {
    return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(ptr()),
                            get_class_object<T>());
  }

private:

  /// @brief Get a type object from the given borrowed PyObject.
  static boost::python::object get_type(boost::python::object object)
  {
    return PyType_Check(object.ptr())
      ? object 
      : object.attr("__class__");
  }

  /// @brief Get the Python class object for C++ type T.
  template <typename T>
  static PyTypeObject* get_class_object()
  {
    namespace python = boost::python;
    // Locate registration based on the C++ type.
    const python::converter::registration* registration =
          python::converter::registry::query(python::type_id<T>());

    // If registration exists, then return the class object.  Otherwise,
    // return NULL.
    return (registration) ? registration->get_class_object()
                          : NULL;
  }
};

/// @brief Enable automatic conversions to type_object.
struct enable_type_object
{
  enable_type_object()
  {
    boost::python::converter::registry::push_back(
      &convertible,
      &construct,
      boost::python::type_id<type_object>());
  }

  static void* convertible(PyObject* object)
  {
    return (PyType_Check(object) || Py_TYPE(object)) ? object : NULL;
  }

  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<type_object>
                                                                 storage_type;
    void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;

    // Construct the type object within the storage.  Object is a borrowed 
    // reference, so create a handle indicting it is borrowed for proper
    // reference counting.
    python::handle<> handle(python::borrowed(object));
    new (storage) type_object(python::object(handle));

    // Set convertible to indicate success. 
    data->convertible = storage;
  }
};

// Mockup types.
struct A {};
struct B: public A {};
struct C {};

/// Mockup function that receives an object's type.
int func(type_object type)
{
  if (type.is<A>()) return 0;
  if (type.is<B>()) return 1;
  return -1;
}

/// Mockup function that returns true if the provided object type is a
/// subclass of A.
bool isSubclassA(type_object type)
{
  return type.is_subclass<A>();
}

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

  // Enable receiving type_object as arguments.
  enable_type_object();

  python::class_<A>("A");
  python::class_<B, python::bases<A> >("B");
  python::class_<C>("C");

  python::def("func", &func);
  python::def("isSubclassA", &isSubclassA);
}

互动用法:

代码语言:javascript
复制
>>> import example
>>> assert(example.func(type("test")) == -1)
>>> assert(example.func(example.A) == 0)
>>> assert(example.func(example.B) == 1)
>>> assert(example.isSubclassA(example.A))
>>> assert(example.isSubclassA(example.B))
>>> assert(not example.isSubclassA(example.C))
>>> assert(example.func("test") == -1)
>>> assert(example.func(example.A()) == 0)
>>> assert(example.func(example.B()) == 1)
>>> assert(example.isSubclassA(example.A()))
>>> assert(example.isSubclassA(example.B()))
>>> assert(not example.isSubclassA(example.C()))
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23393063

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档