Skip to content

Passing a Template Instance to a Python Callback by Reference causes Segmentation Fault #321

@ClemensHoppenau

Description

@ClemensHoppenau

Hi,

I encountered this issue when trying to pass a reference to a std::optional to a callback defined in python.
The follwing example reproduces the issue:

import cppyy
import cppyy.ll

src ="""
    #include <functional>
    #include <optional>

    void fn(std::function<void(std::optional<int>&)> callback) {
        auto optional = std::optional<int>(42);
        std::cout << "cpp: &optional = " << &optional << std::endl;
        callback(optional);
    }
"""
cppyy.cppdef(src)

def callback(optional):
    print("py:  &optional =", hex(cppyy.ll.addressof(optional)))
    print("optional.value() =", optional.value())

cppyy.gbl.fn(callback)
$ python3 test.py 
cpp: &optional = 0x7fff27ffe960
py:  &optional = 0x79010000002a
 *** Break *** segmentation violation
#0  0x000079401dec67a7 in __GI___wait4 (pid=144749, stat_loc=stat_loc
entry=0x7fff27ffafa8, options=options
entry=0, usage=usage
entry=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30
#1  0x000079401dec68eb in __GI___waitpid (pid=<optimized out>, stat_loc=stat_loc
entry=0x7fff27ffafa8, options=options
entry=0) at ./posix/waitpid.c:38
#2  0x000079401de0e5bb in do_system (line=<optimized out>) at ../sysdeps/posix/system.c:172
#3  0x000079401d59bc65 in CppyyLegacy::TUnixSystem::StackTrace() () from /workspaces/middleware-py/venv2/lib/python3.12/site-packages/cppyy_backend/lib/libCoreLegacy.so
#4  0x000079401cdd6b23 in (anonymous namespace)::do_trace (sig=1) at src/clingwrapper.cxx:267
#5  (anonymous namespace)::TExceptionHandlerImp::HandleException (this=<optimized out>, sig=1) at src/clingwrapper.cxx:280
#6  0x000079401d59a741 in CppyyLegacy::TUnixSystem::DispatchSignals(CppyyLegacy::ESignals) () from /workspaces/middleware-py/venv2/lib/python3.12/site-packages/cppyy_backend/lib/libCoreLegacy.so
#7  <signal handler called>
#8  0x000079401d70a004 in ?? ()
#9  0x0000000000000000 in ?? ()
 *** Break *** segmentation violation
#0  0x000079401dec67a7 in __GI___wait4 (pid=144842, stat_loc=stat_loc
entry=0x7fff27ffafa8, options=options
entry=0, usage=usage
entry=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30
#1  0x000079401dec68eb in __GI___waitpid (pid=<optimized out>, stat_loc=stat_loc
entry=0x7fff27ffafa8, options=options
entry=0) at ./posix/waitpid.c:38
#2  0x000079401de0e5bb in do_system (line=<optimized out>) at ../sysdeps/posix/system.c:172
#3  0x000079401d59bc65 in CppyyLegacy::TUnixSystem::StackTrace() () from /workspaces/middleware-py/venv2/lib/python3.12/site-packages/cppyy_backend/lib/libCoreLegacy.so
#4  0x000079401cdd69a7 in (anonymous namespace)::do_trace (sig=1) at src/clingwrapper.cxx:267
#5  (anonymous namespace)::TExceptionHandlerImp::HandleException (this=<optimized out>, sig=1) at src/clingwrapper.cxx:286
#6  0x000079401d59a741 in CppyyLegacy::TUnixSystem::DispatchSignals(CppyyLegacy::ESignals) () from /workspaces/middleware-py/venv2/lib/python3.12/site-packages/cppyy_backend/lib/libCoreLegacy.so
#7  <signal handler called>
#8  0x000079401d70a004 in ?? ()
#9  0x0000000000000000 in ?? ()

The problem seems to also apply to other templated types. The following code also segfaults:

import cppyy
import cppyy.ll

src ="""
    #include <functional>

    template<typename T>
    struct Foo {
        T value;
    };

    void fn(std::function<void(Foo<int>&)> callback) {
        auto arg = Foo<int>(42);
        std::cout << "cpp: &arg = " << &arg << std::endl;
        callback(arg);
    }
"""
cppyy.cppdef(src)

def callback(arg):
    print("py:  &arg =", hex(cppyy.ll.addressof(arg)))
    print("arg.value() =", arg.value)

cppyy.gbl.fn(callback)
cpp: &arg = 0x7ffc1205c090
py:  &arg = 0x7cd30000002a
 *** Break *** segmentation violation
...

The code also segfaults with the following changes:

  • using a type alias for the callback parameter
  • using a function pointer instead of a std::function
  • passing the parameter by const reference

Passing the parameter by value or pointer works as expected.
The code also works when Foo is a plain struct instead of a template.

As you can see in the output, the address of arg in python differs from the address of the original arg in C++, I don't know whether this is expected or an error.

I am using cppyy version 3.5.0, installed with pip in a fresh virtual environment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions