Skip to content

[MSVC] Virtual Inheritances (Diamond pattern) causes crash #1256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
yeganer opened this issue Jan 17, 2018 · 0 comments
Open

[MSVC] Virtual Inheritances (Diamond pattern) causes crash #1256

yeganer opened this issue Jan 17, 2018 · 0 comments

Comments

@yeganer
Copy link

yeganer commented Jan 17, 2018

Issue description

I'm wrapping an interface (abstract base class) with pybind. This interface is not meant to be instantiated in python, it's only purpose is showing the inheritance (and if possible wrapping the functions such that they get forwarded to the children). All calls on this Interface take Interface*/Interface&/shared_ptr(doesn't matter, crashes for all). Classes that implement this interface usepublich virtual Interfaceto inherit the Interface. When calling a wrapped function that takes anInterface*with a python instance of a derived object, the C++ pointer loses it's vtable and calling a method on this pointer causes a segmentation fault. Removing thevirtual` part of the inheritance solves the problem (but that is not an option in my case).

The code compiles and runs fine with gcc but fails with Visual Studio 2017 Win64

Reproducible example code

#include <memory>

#include <pybind11/pybind11.h>

namespace py = pybind11;

class Base {
 public:
  virtual ~Base() = default;
  virtual int foo() = 0;
  // virtual int bar(std::shared_ptr<Base> ptr) = 0;
  virtual int bar(Base& ptr) = 0;
  virtual int bar(Base* ptr) = 0;
};

# Remove the 'virtual' in the next line and it will won't crash anymore
class Derived : public virtual Base {
 public:
  virtual ~Derived() = default;
  // int bar(std::shared_ptr<Base> ptr) override { return ptr->foo() + 88; }
  int bar(Base* ptr) override { return ptr->foo() + 88; }
  int bar(Base& ptr) override { return ptr.foo() + 88; }
  int foo() override { return 44; }
};

PYBIND11_MODULE(test_foo, m) {
  py::class_<Base
             //,std::shared_ptr<Base>
             >(m,
               "Base");  // .def("bar", &Base::bar);

  py::class_<Derived, Base
             //, std::shared_ptr<Derived>
             >(m, "Derived")
      .def(py::init<>());
  //.def("bar", &Derived::bar);

  // m.def("get_sptr", [](std::shared_ptr<Base> b) { return b->bar(b); });
  m.def("get_ref", [](Base& b) { return b.bar(b); });
  m.def("get_ptr", [](Base* b) { return b->bar(b); });
  // m.def("run_cpp", []() {
  //  auto b = std::make_shared<Derived>();
  //  return b->bar(b);
  //});
}

Python code

import test_foo

# This version works for shared pointers
#print(test_foo.run_cpp())

d = test_foo.Derived()
print(test_foo.get_ptr(d))
print(test_foo.get_ref(d))
#print(test_foo.get_sptr(d))
jagerman added a commit to yeganer/pybind11 that referenced this issue Jan 18, 2018
We currently automatically detect multiple inheritance (when registered
with pybind) to enable non-simple casting, but simple casting is also
invalid when there is single-base, virtual inheritance (pybind#1256).  (This
happens to work under gcc/clang, but makes MSVC segfault when upcasting).

This commit adds detection for a specified base class being a virtual
base, and if so, turns on the `multiple_inheritance` flag to mark the
class and base as non-simple, and adds tests (which segfault under MSVC
without this fix).

(Earlier versions can work around the issue by explicitly passing a
`py::multiple_inheritance{}` option to the `py::class_<...>` constructor).

Co-authored-by: Jason Rhinelander <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant