5

I have an Event class defined in C++ that I expose to Python using Boost. My scripts are expected to derive from this class, and I'd like to do some initialization whenever a new child class is defined.

How can I set the metaclass of the exposed Event class such that whenever a Python script derives from this class, the metaclass could do the required initialization?

I would like to avoid having to explicitly use a metaclass in the scripts...

class KeyboardEvent(Event):  # This is what I want
    pass

class KeyboardEvent(Event, metaclass=EventMeta): # This is not a good solution
    pass

Edit: Part of the solution

It seems there's no way to set the metaclass with Boost.Python. The next best thing is to improvise and change the metaclass after the class was defined. In native Python, the safe way to change a metaclass is to do this:

B = MetaClass(B.__name__, B.__bases__, B.__dict__)

In Boost, it'd look something like this:

BOOST_PYTHON_MODULE(event)
{
    using namespace boost::python;
    using boost::python::objects::add_to_namespace;

    class_<EventMetaClass> eventmeta("__EventMetaClass")
        ...;

    class_<Event> event("Event")
        ...;

    add_to_namespace(scope(), "Event",
        eventmeta(event["__name__"], event["__bases__"], event["__dict__"]));
}

The problem is that I can't seem to find a way to define a metaclass with Boost.Python, which is why I've opened How to define a Python metaclass with Boost.Python?.

Community
  • 1
  • 1
Paul Manta
  • 30,618
  • 31
  • 128
  • 208
  • Maybe `class EventWithMeta(Event, metaclass=EventMeta):pass` then `class KeyboardEvent(EventWithMeta); class AnotherEvent(EventWithMeta)`? – reclosedev Jan 27 '12 at 18:29
  • @reclosedev No. The problem isn't me typing the "metaclass" word. It's that the need for a metaclass is an implementation detail that shouldn't leak to the interface exposed to scripts. – Paul Manta Jan 27 '12 at 18:31
  • Maybe I'm missing something, but in this case `Event` can be renamed to `_Event` or something like this, and `EventWithMeta` to `Event`. – reclosedev Jan 27 '12 at 18:48
  • @reclosedev That's one obvious solution, but it has many disadvantages in my case. – Paul Manta Jan 27 '12 at 19:05
  • Changing the metaclass of a class makes just as much sense as changing the class of an instance, i.e. you can do it but it very rarely results in anything usable. What are the many disadvantages of doing it the way reclosedev suggests? – Ben Jan 31 '12 at 01:36
  • @Ben Well, in this case I'm not really changing the class, I'm constructing an entirely new type. Doing what reclosedev suggested would scatter the implementation all over the place. I'm embedding Python, not extending it, so all the functionality should be already there, there shouldn't be a script that fixes the C++ defined stuff. – Paul Manta Jan 31 '12 at 06:54

1 Answers1

0

If boost does not offer a way to do it from withn c++, and it looks like it don't, the way to go is to create wrapper classes that implement the metaclass -

It can be done more or less automatically usign a ittle bit of instrospection. Let's suppose your boost module is named "event" - you should either name the file as _event or place it inside you module, and write an python file - named "event.py" (or an __init__.py file on your module that would do more or less this:

import _event

class eventmeta(type):
    ...

event_dict = globals()
for key, value in _event.__dict__.items():
    if isinstance(value, type):
        event_dict[key] = eventmeta(key, (value,),{})
    else:
        #set other module members as members of this module
        event_dict[key] = value

del key, value, event_dict

Thos cpde will automatically set module variables equal to any names found in the native"_event" module - and for each class it encounters, create a new class changing the metaclass, as in your example.

It may be that you get a metaclass conflict by doing this. If so, the way is to make the newly created classes to be proxies to the native classes, by creating proper __getattribute__ and __setattr__ methods. Just ask in a comment if you will need to do that.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • I can't define the metaclass in Python, unfortunately. The initialization it needs to do is to make a custom RTTI system work with C++ events as well as Python events. I could expose enough C++ implementation details to Python, so the script could do the required initialization, but that's just not a good solution. – Paul Manta Jan 31 '12 at 06:57