0

Ok. My question is actually quite simple. This is a frame listener class and python wrapper class that is then used by boost python:

class FrameListener
    {
    public:

        struct FrameEvent
        {
            double TimeSinceLastEvent;
            double TimeSinceLastFrame;
        };

        FrameListener() {}
        ~FrameListener() {}

        virtual void FrameStart(FrameEvent& evt) = 0;
        virtual void FrameEnd(FrameEvent& evt) = 0;
        virtual void OnFrame(FrameEvent& evt) = 0;
    };

    class FrameListener_PyWrapper : public FrameListener, public boost::python::wrapper<FrameListener>
    {
    public:
        FrameListener_PyWrapper() {}    
        ~FrameListener_PyWrapper();

        void FrameStart(FrameEvent& evt);
        void FrameEnd(FrameEvent& evt);
        void OnFrame(FrameEvent& evt);
    };

Classes are then wrapped to boost python:

class_<SL_Engine::FrameListener::FrameEvent>("FrameEvent")
        .def_readonly("TimeSinceLastEvent", &SL_Engine::FrameListener::FrameEvent::TimeSinceLastEvent)
        .def_readonly("TimeSinceLastFrame", &SL_Engine::FrameListener::FrameEvent::TimeSinceLastFrame);

    class_<SL_Engine::FrameListener_PyWrapper, boost::noncopyable>("FrameListener")
        .def("OnFrame", pure_virtual(&SL_Engine::FrameListener::OnFrame))
        .def("FrameStart", pure_virtual(&SL_Engine::FrameListener::FrameStart))
        .def("FrameEnd", pure_virtual(&SL_Engine::FrameListener::FrameEnd))
        ;

This can be overriden in python to create own frame listener class:

class SimpleListen(FrameListener):
def __init__(self):
    pass
def FrameEndd(self, evt):
    pass

Then you create instance of the new SimpleListener class and "register" it to the engine with CORE_ENGINE.AddFrameListener() function. The function then uses boost::python::extract() to extract it and put it on std::vector of frame listeners which are then executed every frame. This works perfectly, but if I forget to define base in simple class or misspell it, it obviously results in a crash. I have searched on google both in boost::python or C/Python API, how can I test if base class is properly defined. The AddFrameListener takes PyObject* argument. I want to test if PyObject is class type first. If you accidently try to put an integer value or a string that the program will identify the error and message that the object is not class type. If it is class type I want to test if it has base class and if it can be extracted to FrameListener_PyWrapper*. Later on I want to test each individual function (FrameStart, FrameEnd and OnFrame) if they are overriden in python class and if arguments match. How can I get list of arguments from python function? If any of the functions from python class is not overriden it will give me a warning. If arguments don't match and you try to call:

if (boost::python::override f = this->get_override("FrameEnd"))
    {
        f(evt);
        return;
    }

It will result in a crash without any information about error. I want to check if arguments match when calling AddFrameListener function and save information if any of the functions are not properly overriden the engine will not call them.

1 Answer 1

0

The crash maybe related to the fact you are calling into Python without the GIL locked. You should lock it everytime you interact with a Python Object:

    /*
This RAII structure ensures that threads created on the native C side
adhere to the laws of Python and ensure they grab the GIL lock when
calling into python
*/
struct PyLockGIL
{

    PyLockGIL()
        : gstate(PyGILState_Ensure())
    { 
    }

    ~PyLockGIL()
    {
        PyGILState_Release(gstate);
    }

    PyLockGIL(const PyLockGIL&) = delete;
    PyLockGIL& operator=(const PyLockGIL&) = delete;

    PyGILState_STATE gstate;
};

Then use it before you callback into Python:

    if (override fe = this->get_override("FrameEnd"))
{
    PyLockGIL lock;
    try
    {
        fe();
        return;
    }
    catch (const error_already_set&)
    {
        std::cout << "Exception in FrameEnd in application";
        PyErr_Print();
    }
}
else
{
    std::cout << "FrameEnd called but not overridden";
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.