14

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.

Community
  • 1
  • 1
Paul Manta
  • 30,618
  • 31
  • 128
  • 208
  • Are you only looking for Boost.Python solutions? I think I can solve this with SWIG :) – Flexo Jan 30 '12 at 19:05
  • @awoodland Boost.Python is preferable, but I'm not opposed to SWIG. – Paul Manta Jan 30 '12 at 19:22
  • Ok, if you don't get a good Boost.Python answer ping me to remind me with another @ and I'll take a look at doing it with SWIG. – Flexo Jan 30 '12 at 19:24
  • I'm not sure if this is possible just with c++ as boost python already set the metaclass of exposed types to Boost::Python::class. You might be able to write a class in c++ that has a __new__ methd and then use that as a metaclass in python. – babak Feb 01 '12 at 19:11
  • @babak That would be Good Enough™, but the exposed metaclass still has to inherit from an equivalent of Python's `type`. – Paul Manta Feb 01 '12 at 19:53
  • I think it already does, Boost::Python::class inherits from type – babak Feb 01 '12 at 22:50
  • @babak It doesn't, I checked. – Paul Manta Feb 02 '12 at 17:28
  • Does this help? http://code.activestate.com/lists/python-cplusplus-sig/15826/ – grieve Feb 06 '12 at 19:39

1 Answers1

4

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.

babak
  • 774
  • 3
  • 11
  • I'll try it out. It most likely won't work with Python 3 (which is the version I'd like to use), but I guess I'll downgrade if I have to. – Paul Manta Feb 06 '12 at 21:15
  • if you havent done already it could be worth asking on http://boost.2283326.n4.nabble.com/Python-c-sig-f2696818.html – babak Feb 06 '12 at 22:24
  • I awarded you the bounty because this answer is pretty good, but I won't accept it yet. I'm still looking for a solution that will work for Python 3 as well. If I don't find any I'll accept this answer. – Paul Manta Feb 07 '12 at 06:45