wlav / cppyy

Other
390 stars 40 forks source link

Python callback receives null pointer when argument is an object using templeted inheritance #246

Open darren-harton opened 1 month ago

darren-harton commented 1 month ago

Here's the code to reproduce:

import time

import cppyy
import cppyy.ll

cpp = cppyy.gbl
std = cppyy.gbl.std

cppyy.cppdef("""

struct Robot
{
    size_t idx;
    std::string name;
};

struct MetaData
{
    uint64_t seq{0};
};

/// Inherit explicitly
class MetaRobot : public Robot
{
 public:
  MetaData meta;

  MetaRobot(): Robot(), meta() {}
};

using Robot2 = MetaRobot;

/// Inherit from template param
/// https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design#Policy-based_design
template <typename T>
class MetaWrapper : public T
{
 public:
  MetaData meta;

  MetaWrapper(): T(), meta() {}
};

using Robot3 = MetaWrapper<Robot>;

template <typename T>
class CallbackWrapper
{
public:
    using Type = T;
    using Callback = std::function<void(const T&)>;
    Callback func;

    void call(const T& val)
    {
        std::cout << "in CallbackWrapper " << val.name.size() << " " << std::hex << &val << std::dec << std::endl;
        func(val);
    }
};

template <typename T>
void callback(const T& rs)
{
    std::cout << "in callback " << rs.name.size() << " " << std::hex << &rs << std::dec << std::endl;
};

void cpp_test()
{
    {
        auto rs = Robot();
        rs.name = "cpp hello";
        std::cout << "Robot " << std::hex << &rs << std::dec << std::endl;

        auto wrapper = CallbackWrapper<Robot>();

        wrapper.func = callback<Robot>;
        wrapper.call(rs);
    }

    {
        auto rs = Robot2();
        rs.name = "cpp hello";
        std::cout << "Robot2 " << std::hex << &rs << std::dec << std::endl;

        auto wrapper = CallbackWrapper<Robot2>();

        wrapper.func = callback<Robot2>;
        wrapper.call(rs);
    }

    {
        auto rs = Robot3();
        rs.name = "cpp hello";
        std::cout << "Robot3 " << std::hex << &rs << std::dec << std::endl;

        auto wrapper = CallbackWrapper<Robot3>();

        wrapper.func = callback<Robot3>;
        wrapper.call(rs);
    }

};

""")

Calling cpp.cpp_test() outputs:

Robot 0x7fff6f1c0fe0
in CallbackWrapper 9 0x7fff6f1c0fe0
in callback 9 0x7fff6f1c0fe0
Robot2 0x7fff6f1c0fe0
in CallbackWrapper 9 0x7fff6f1c0fe0
in callback 9 0x7fff6f1c0fe0
Robot3 0x7fff6f1c0fe0
in CallbackWrapper 9 0x7fff6f1c0fe0
in callback 9 0x7fff6f1c0fe0

However, when invoking this though cppyy:

def handle(rs):
    time.sleep(0.001)
    print('in handle', rs)
    print('in handle', rs.name.size())

print('\nRobot')
with cppyy.ll.signals_as_exception():
    rs = cpp.Robot()
    rs.name = "hello"
    print(rs)

    wrapper = cpp.CallbackWrapper[cpp.Robot]()
    wrapper.func = handle
    wrapper.call(rs)

print('\nRobot2')
with cppyy.ll.signals_as_exception():
    rs = cpp.Robot2()
    rs.name = "hello"
    print(rs)

    wrapper = cpp.CallbackWrapper[cpp.Robot2]()
    wrapper.func = handle
    wrapper.call(rs)

print('\nRobot3')
with cppyy.ll.signals_as_exception():
    rs = cpp.Robot3()
    rs.name = "hello"
    print(rs)

    wrapper = cpp.CallbackWrapper[cpp.Robot3]()
    wrapper.func = handle
    wrapper.call(rs)

Outputs:


Robot
<cppyy.gbl.Robot object at 0x558e798b62e0>
in CallbackWrapper 5 0x558e798b62e0
in handle <cppyy.gbl.Robot object at 0x558e798b62e0>
in handle 5

Robot2
<cppyy.gbl.MetaRobot object at 0x558e7a08ce50>
in CallbackWrapper 5 0x558e7a08ce50
in handle <cppyy.gbl.MetaRobot object at 0x558e7a08ce50>
in handle 5

Robot3
<cppyy.gbl.MetaWrapper<Robot> object at 0x558e7a394800>
in CallbackWrapper 5 0x558e7a394800
in handle <cppyy.gbl.MetaWrapper<Robot>* object at 0x(nil)>

---------------------------------------------------------------------------
ReferenceError                            Traceback (most recent call last)
Cell In[2], line 35
     33 wrapper = cpp.CallbackWrapper[cpp.Robot3]()
     34 wrapper.func = handle
---> 35 wrapper.call(rs)

ReferenceError: void CallbackWrapper<MetaWrapper<Robot>>::call(const MetaWrapper<Robot>& val) =>
    ReferenceError: attempt to access a null-pointer