简体   繁体   中英

Setting metaclass of wrapped class with Boost.Python

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? .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM