Python C API - 如何从 PyObject 构造对象
Python C API - How to construct object from PyObject
我正在寻找是否有一种很好的 "native" 方法来构造给定 PyObject*
已知类型的对象。
这是我的代码:
C++
void add_component(boost::python::object& type)
{
auto constructed_type = type(); // doesn't construct anything!
}
Python
o = GameObject()
o.add_component(CameraComponent)
我的代码完美无缺地执行了整个函数,但从未为 CameraComponent
.
触发构造函数
所以我的问题是,给定一个已知类型的 PyObject*
,我如何构建该类型的实例?
非常感谢。
这实际上一直有效,但编译器以某种方式优化了构造函数逻辑,所以我的断点从未被击中!
如果 boost::python::object
引用了一个类型,那么调用它将构造一个具有引用类型的对象:
boost::python::object type = /* Py_TYPE */;
boost::python::object object = type(); // isinstance(object, type) == True
由于 Python 中的几乎所有内容都是对象,接受来自 Python 的参数作为 boost::python::object
将允许任何类型的对象,即使那些不是类型的对象。只要对象是可调用的(__call___
),那么代码就会成功。
另一方面,如果你想保证提供了一个类型,那么一个解决方案是创建一个表示 Python 类型的 C++ 类型,接受它作为参数,并使用自定义转换器仅在提供 Python 类型时构造 C++ 类型。
以下 type_object
C++ 类型表示一个 Python 对象,它是一个 Py_TYPE
.
/// @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(object)
{
if (!PyType_Check(object.ptr()))
{
throw std::invalid_argument("type_object requires a Python type");
}
}
};
...
// Only accepts a Python type.
void add_component(type_object type) { ... }
以下自定义转换器将仅构造一个 type_object
实例,前提是它提供的 PyObject*
是 Py_TYPE
:
/// @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) ? 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;
}
};
...
BOOST_PYTHON_MODULE(...)
{
enable_type_object(); // register type_object converter.
}
这里是一个完整的例子 demonstrating 公开一个需要 Python 类型的函数,然后构造一个类型的实例:
#include <iostream>
#include <stdexcept> // std::invalid_argument
#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(object)
{
if (!PyType_Check(object.ptr()))
{
throw std::invalid_argument("type_object requires a Python type");
}
}
};
/// @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) ? 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;
}
};
// Mock API.
struct GameObject {};
struct CameraComponent
{
CameraComponent()
{
std::cout << "CameraComponent()" << std::endl;
}
};
boost::python::object add_component(GameObject& /* self */, type_object type)
{
auto constructed_type = type();
return constructed_type;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Enable receiving type_object as arguments.
enable_type_object();
python::class_<GameObject>("GameObject")
.def("add_component", &add_component);
python::class_<CameraComponent>("CameraComponent");
}
交互使用:
>>> import example
>>> game = example.GameObject()
>>> component = game.add_component(example.CameraComponent)
CameraComponent()
>>> assert(isinstance(component, example.CameraComponent))
>>> try:
... game.add_component(component) # throws Boost.Python.ArgumentError
... assert(False)
... except TypeError:
... assert(True)
...
我正在寻找是否有一种很好的 "native" 方法来构造给定 PyObject*
已知类型的对象。
这是我的代码:
C++
void add_component(boost::python::object& type)
{
auto constructed_type = type(); // doesn't construct anything!
}
Python
o = GameObject()
o.add_component(CameraComponent)
我的代码完美无缺地执行了整个函数,但从未为 CameraComponent
.
所以我的问题是,给定一个已知类型的 PyObject*
,我如何构建该类型的实例?
非常感谢。
这实际上一直有效,但编译器以某种方式优化了构造函数逻辑,所以我的断点从未被击中!
如果 boost::python::object
引用了一个类型,那么调用它将构造一个具有引用类型的对象:
boost::python::object type = /* Py_TYPE */;
boost::python::object object = type(); // isinstance(object, type) == True
由于 Python 中的几乎所有内容都是对象,接受来自 Python 的参数作为 boost::python::object
将允许任何类型的对象,即使那些不是类型的对象。只要对象是可调用的(__call___
),那么代码就会成功。
另一方面,如果你想保证提供了一个类型,那么一个解决方案是创建一个表示 Python 类型的 C++ 类型,接受它作为参数,并使用自定义转换器仅在提供 Python 类型时构造 C++ 类型。
以下 type_object
C++ 类型表示一个 Python 对象,它是一个 Py_TYPE
.
/// @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(object)
{
if (!PyType_Check(object.ptr()))
{
throw std::invalid_argument("type_object requires a Python type");
}
}
};
...
// Only accepts a Python type.
void add_component(type_object type) { ... }
以下自定义转换器将仅构造一个 type_object
实例,前提是它提供的 PyObject*
是 Py_TYPE
:
/// @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) ? 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;
}
};
...
BOOST_PYTHON_MODULE(...)
{
enable_type_object(); // register type_object converter.
}
这里是一个完整的例子 demonstrating 公开一个需要 Python 类型的函数,然后构造一个类型的实例:
#include <iostream>
#include <stdexcept> // std::invalid_argument
#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(object)
{
if (!PyType_Check(object.ptr()))
{
throw std::invalid_argument("type_object requires a Python type");
}
}
};
/// @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) ? 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;
}
};
// Mock API.
struct GameObject {};
struct CameraComponent
{
CameraComponent()
{
std::cout << "CameraComponent()" << std::endl;
}
};
boost::python::object add_component(GameObject& /* self */, type_object type)
{
auto constructed_type = type();
return constructed_type;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Enable receiving type_object as arguments.
enable_type_object();
python::class_<GameObject>("GameObject")
.def("add_component", &add_component);
python::class_<CameraComponent>("CameraComponent");
}
交互使用:
>>> import example
>>> game = example.GameObject()
>>> component = game.add_component(example.CameraComponent)
CameraComponent()
>>> assert(isinstance(component, example.CameraComponent))
>>> try:
... game.add_component(component) # throws Boost.Python.ArgumentError
... assert(False)
... except TypeError:
... assert(True)
...