How should I control the lifetime of a python callable from c++?

39 Views Asked by At

I have code that wishes to call a python function from C++. Based on suggestions from https://cppyy.readthedocs.io/en/latest/functions.html#callbacks I wrote this:

ns.cppyy.cppdef("""
        #include "CPyCppyy/API.h"

        using namespace ns3;
        EventImpl* BoundFunctionEvent(void (*function)(void))
        {
            std::cout << "binding" << std::endl;
            return MakeEvent(function);
        }
       """)

def _schedule(delay, callable, *args, **kwargs):
    bound_callable = functools.partial(callable, *args, **kwargs)                                                                                       
    ns.core.Simulator.Schedule(delay, ns.cppyy.gbl.BoundFunctionEvent(bound_callable))

def hello():
    print('hello')

_schedule(ns.core.Seconds(1.0), hello)
ns.core.Simulator.Run()

Now, if I run the above, I get this backtrace:

Traceback (most recent call last):
  File "/source/ns-3-dev/build/./ns3.py", line 58, in <module>
    main()
  File "/source/ns-3-dev/build/./ns3.py", line 55, in main
    args.func(args)
  File "/source/ns-3-dev/build/./ns3.py", line 42, in run
    ns.core.Simulator.Run()
TypeError: static void ns3::Simulator::Run() =>
    TypeError: callable was deleted

Which, really, makes perfect sense: yes, the python callable went out of scope when the C++ code tries to call it later.

The question is: how can I convince the binding/C++ code to take ownership of the callable so it still lives when it is called by C++ ?

1

There are 1 best solutions below

0
On

I did not find an answer to the exact question I asked but I did find a solution to my problem so, here it is in the hope this helps whoever has a similar question.

In my case, it just so happened that the C++ code I needed to hand over my callable to defined a C++ abstract base class that wrapped the functor so, all I had to do was to wrap my python callable in a python subclass of the event abstract base class (which, really, I had no idea was actually possible with cppyy: amazing piece of technology !). This looks like the following:

class Event(ns.core.EventImpl):                                                                                                                         
    def __init__(self, callable):
        super(Event, self).__init__()
        self._callable = callable

    def Notify(self):
        self._callable()


def make_event(callable, *args, **kwargs):
    bound_callable = functools.partial(callable, *args, **kwargs)
    e = Event(bound_callable)
    e.__python_owns__ = False
    return e
              
def _schedule(delay, callable, *args, **kwargs):
    e = make_event(callable, *args, **kwargs)
    ns.core.Simulator.Schedule(delay, e)

Notice above the __python_owns__ = False that does for this object what I was trying to do for my python callable in my initial question.