The Python C API has the PyObject *PyType_Type
object, which is equivalent to type
in the interpreter. If I want to define a metaclass in C++, how can I set type
as one of its bases in Boost.Python? Also, what other things should I take into consideration when defining a Python metaclass in C++?
It'd be ideal if there was a Boost.Python solution to this. If not, a solution that uses the Python C API (or a combination of Boost and the C API) is good as well. Since my other classes are exposed with Boost, I'd rather leave SWIG as a last resort.
Note: This is actually part of a bigger problem I'm trying to solve, which I've asked about in Setting metaclass of wrapped class with Boost.Python, if you're interested.
Okay this feels like a hack but it seems to work.
#include <boost/python.hpp>
class Meta
{
public:
static boost::python::object
newClass(boost::python::object cls, std::string name, boost::python::tuple bases, boost::python::dict attrs)
{
attrs["foo"] = "bar";
boost::python::object types = boost::python::import("types");
boost::python::object type = types.attr("TypeType");
return type.attr("__new__")(type, name, bases, attrs);
}
};
BOOST_PYTHON_MODULE(meta)
{
boost::python::class_<Meta>("Meta")
.def("__new__", &Meta::newClass)
.staticmethod("__new__");
}
then in python
from meta import Meta
class Test(object):
__metaclass__ = Meta
print Test, Test.foo
<class '__main__.Test'> bar
I tried some other things which didn't use the boost::python::object system but couldn't get anything that worked like this from the python side.
Although strictly speaking this isnt a metaclass as it doesnt inherit from type, but it behaves like one because type is used directly in the newClass function when calling new. If thats not a problem then it might be wise to change it from
return type.attr("__new__")(type, name, bases, attrs);
to
return type.attr("__new__")(cls.attr("__class__"), name, bases, attrs);
or something similar so Boost::Python::class is used instead of type.