将类型对象(类,而不是实例)从python传递给c ++ [英] Pass a type object (class, not an instance) from python to c++
问题描述
我想拥有一个boost::python-wrapped
c ++函数,该函数能够接收类型(而不是实例),一个boost::python
包裹的c ++类.我可以声明采用object
的包装函数,但是如何提取类型呢?我尝试过类似的操作,但类型对象似乎不支持extract
:
I would like to have a boost::python-wrapped
c++ function which is able to receive type (rather than an instance), a boost::python
-wrapped c++ class. I can declare the wrapped function taking an object
, but how to extract the type? I tried something like this, but type objects don't seem to be extract
-able:
#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");
}
编译为
clang++ -lboost_python -fPIC `pkg-config python --cflags` a.cc -std=c++11 -shared -o deadbeef.so
我跑步
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
感谢任何想法.
推荐答案
要传递Python类型对象,需要创建一个C ++类型并注册一个自定义自定义转换器.由于Python类型对象是python对象,因此创建从boost::python::object
派生的类型是合适的:
To pass a Python type object, one needs to create a C++ type and register a custom a custom converter. As a Python type object is a python object, creating a type that derives from boost::python::object
is appropriate:
/// @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类型对象.
However, the example code presents an additional problem. One cannot directly perform comparisons between a Python type object and a C++ type. Furthermore, The Python type object has no direct association with the C++ type. To perform comparisons, one needs to compare the Python type objects.
Boost.Python使用内部注册表来关联C ++类型身份,格式为
Boost.Python uses an internal registry to associate C++ type identity, in the form of boost::python::type_info
, to a Python class object. This association is one-way, in that one can only lookup a Python class object. Lets expand the type_object
class to allow to provide auxiliaries functions for checking against C++ types:
/// @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;
}
};
现在,如果type
是type_object
的实例,则可以检查:
Now, if type
is an instance of type_object
, one could check:
- 如果
type
是与具有type.is<Spam>()
的C ++Spam
类型关联的Python类型. - 如果
type
是与type.is_subclass<Spam>()
的C ++Spam
类型关联的Python类型的子类.
- If
type
is the Python type associated with the C++Spam
type withtype.is<Spam>()
. - If
type
is a subclass of the Python type associated with the C++Spam
type withtype.is_subclass<Spam>()
.
这是一个基于原始代码的完整示例,演示接收类型对象函数,检查类型标识和子类:
Here is a complete example based on the original code that demonstrates receiving type objects to functions, checking for type identity and subclasses:
#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);
}
互动用法:
>>> 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()))
这篇关于将类型对象(类,而不是实例)从python传递给c ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!