Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a Python metaclass with Boost.Python?

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.

like image 357
Paul Manta Avatar asked Jan 29 '12 04:01

Paul Manta


1 Answers

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.

like image 74
babak Avatar answered Sep 18 '22 13:09

babak