Skip to content

multiply inherited C++ classes #181

@dellaert

Description

@dellaert

Title: pybind wrapper needs safe handling for inherited methods on multiply inherited C++ classes

Summary

We found a pybind wrapper bug affecting inherited non-virtual base methods when the wrapped C++ class has multiple inheritance but the generated binding only lists one base and does not use py::multiple_inheritance().

Concrete case from GTSAM legged estimators:

  • gtsam::LeggedEstimator declares non-virtual methods:
    • void turnHeightPriorOn(double terrainHeight);
    • void turnHeightPriorOff();
  • gtsam::LeggedInvariantEKF inherits from:
    • LeftLinearEKF<ExtendedPose3d>
    • LeggedEstimator

Observed behavior

When turnHeightPriorOn/Off() were exposed only on the abstract base LeggedEstimator in the .i file, Python calls on concrete LeggedInvariantEKF instances had no behavioral effect.

Workaround that fixed it immediately:

  • redeclare the same methods on each concrete wrapped class in the .i file

That strongly suggests the issue is wrapper inheritance / base-pointer adjustment, not estimator logic.

pybind11 docs say this is undefined behavior unless:

  • all C++ bases are listed, or
  • py::multiple_inheritance() is supplied

Minimal reproduction pattern

  • abstract base class with non-virtual method
  • derived class with multiple C++ bases
  • wrapper lists only one base
  • base method is only bound on the base class
  • calling that method from Python on a concrete object does not affect behavior correctly
  • rebinding the same method directly on the concrete class works around the problem

The smallest fix that appears correct is:

  • emit py::multiple_inheritance() for classes with a base class in generated pybind bindings

Possible better long-term fixes:

  • model multiple bases explicitly in the interface language
  • validate .i inheritance against the real C++ inheritance
  • emit py::multiple_inheritance() only when truly needed, if that can be detected reliably

Regression test suggestion

Add a small pybind integration test with:

  • base class method
  • multiply inherited derived class
  • binding lists one base
  • verify that inherited method on derived object behaves correctly only when py::multiple_inheritance() is present

Why this matters

Without this fix, downstream projects have to duplicate inherited methods on concrete classes in .i files as a workaround, which is brittle and obscures the real wrapper problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions