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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions