Skip to content

SWIG 4.4.0 breaks Python and Ruby bindings for methods taking std::vector<T> where T is only forward-declared in the defining .i module #5645

Description

@jmarrec

Description

NatLabRockies/OpenStudio-resources#230 (comment)

For eg ShadowCalculation.addShadingZoneGroup(ThermalZoneVector) no longer works with swig 4.4.0

From 3.11.0 to develop today (3.12.0-alpha), SWIG bumped from 4.1.1 → 4.4.0 (conanfile.py).

Python calls to at least two methods now fail with a TypeError, even when passing the "correct" *Vector wrapper type that used to work:

TypeError: in method 'ShadowCalculation_addShadingZoneGroup', argument 2 of type 'std::vector<openstudio::model::ThermalZone,...> const &'
TypeError: in method 'ShadingControl_addSubSurfaces', argument 2 of type 'std::vector<openstudio::model::SubSurface,...> const &'

Confirmed via a minimal repro (openstudio.model.ShadowCalculation().addShadingZoneGroup(...)) run against both /usr/local/openstudio-3.11.0 (SWIG 4.1.1, passes) and a 26.1.0 dev build (SWIG 4.4.0, fails) — same code, same OpenStudio Python API, only the SWIG version differs. Neither a ThermalZoneVector() wrapper nor a plain Python list works on the newer build; both worked (Vector) or failed the same way (list) on 3.11.0.

Current Behavior

No longer works.

Expected Behavior

Used to work at 3.11.0

Steps to reproduce

swig_vector_repro.py

openstudio swig_vector_repro.py

#!/usr/bin/env python3
"""
Minimal reproducer for the SWIG vector-argument TypeError seen in
shadowcalculation.rb when run against newer OpenStudio builds
(SWIG 4.1.1 -> 4.4.0 bump).

Pattern: build a ThermalZoneVector wrapper object, append elements to it,
then pass it to a method whose C++ signature expects
`const std::vector<T> &`. In older SWIG this implicit conversion worked;
in SWIG 4.4.0 it appears to raise a TypeError instead. Also tries passing
a plain Python list, to see if that's accepted as a fallback.

Run with: <openstudio_cli> swig_vector_repro.py
"""
import openstudio

print(f"OpenStudio version: {openstudio.openStudioVersion()}")

model = openstudio.model.Model()

zone = openstudio.model.ThermalZone(model)
sc = model.getShadowCalculation()

zone_vector = openstudio.model.ThermalZoneVector()
zone_vector.append(zone)

try:
    sc.addShadingZoneGroup(zone_vector)
    print("PASS: ShadowCalculation.addShadingZoneGroup(ThermalZoneVector) worked")
except TypeError as e:
    print(f"FAIL: ShadowCalculation.addShadingZoneGroup(ThermalZoneVector) raised TypeError: {e}")

sc.removeAllShadingZoneGroups()

try:
    sc.addShadingZoneGroup([zone])
    print("PASS: ShadowCalculation.addShadingZoneGroup([zone]) worked")
except TypeError as e:
    print(f"FAIL: ShadowCalculation.addShadingZoneGroup([zone]) raised TypeError: {e}")

# Sanity check: does a plain list work for other vector-typed APIs?
# (eg constructing a Surface from a plain list of Point3d, where the C++
# signature is `Surface(const std::vector<Point3d>&, const Model&)`)
try:
    space = openstudio.model.Space(model)
    pts = [
        openstudio.Point3d(0, 0, 0),
        openstudio.Point3d(1, 0, 0),
        openstudio.Point3d(1, 1, 0),
        openstudio.Point3d(0, 1, 0),
    ]
    surface = openstudio.model.Surface(pts, model)
    print("PASS: Surface(list_of_Point3d, model) worked")
except TypeError as e:
    print(f"FAIL: Surface(list_of_Point3d, model) raised TypeError: {e}")

# Another ModelObject-subclass vector: AirLoopHVAC.setAvailabilityManagers(std::vector<AvailabilityManager>)
# called with a plain list in model/simulationtests/availability_managers.py:34
try:
    air_loop = openstudio.model.AirLoopHVAC(model)
    avm = openstudio.model.AvailabilityManagerNightVentilation(model)
    air_loop.setAvailabilityManagers([avm])
    print("PASS: AirLoopHVAC.setAvailabilityManagers([avm]) worked")
except TypeError as e:
    print(f"FAIL: AirLoopHVAC.setAvailabilityManagers([avm]) raised TypeError: {e}")
--- /usr/local/openstudio-3.11.0/bin/openstudio swig_vector_repro.py
+++ ./Products/openstudio swig_vector_repro.py
- OpenStudio version: 3.11.0
+ OpenStudio version: 3.12.0
-PASS: ShadowCalculation.addShadingZoneGroup(ThermalZoneVector) worked
+FAIL: ShadowCalculation.addShadingZoneGroup(ThermalZoneVector) raised TypeError: in method 'ShadowCalculation_addShadingZoneGroup', argument 2 of type 'std::vector< openstudio::model::ThermalZone,std::allocator< openstudio::model::ThermalZone > > const &'
FAIL: ShadowCalculation.addShadingZoneGroup([zone]) raised TypeError: in method 'ShadowCalculation_addShadingZoneGroup', argument 2 of type 'std::vector< openstudio::model::ThermalZone,std::allocator< openstudio::model::ThermalZone > > const &'
PASS: Surface(list_of_Point3d, model) worked
PASS: AirLoopHVAC.setAvailabilityManagers([avm]) worked

Possible Solution

Suspected root cause

Both affected methods share the same structural shape: they take std::vector<T>& where T is only forward-declared in the .i file defining the method's class, while T's real class and its %template(...Vector) instantiation live in a separate, dependent SWIG module that imports the defining module:

  • ShadowCalculation::addShadingZoneGroup(std::vector<ThermalZone>&)ShadowCalculation is in ModelSimulation.i; ThermalZone is only forward-declared there (class ThermalZone;), with the real type + ThermalZoneVector template in ModelHVAC.i, which imports ModelSimulation.i.
  • ShadingControl::addSubSurfaces(std::vector<SubSurface>&) — same pattern: ShadingControl in ModelResources.i, SubSurface forward-declared there, real type + SubSurfaceVector in ModelGeometry.i, which imports ModelResources.i.

SWIG 4.1.1 apparently resolved this cross-module reference correctly at wrapper-generation time; SWIG 4.4.0 does not. Other vector-argument methods where the class and its vector-element type live in the same module (e.g. AirLoopHVAC::setAvailabilityManagers(std::vector<AvailabilityManager>&), both in ModelHVAC.i) are unaffected.

Operating System affected

All

Environment

N/A

Version of OpenStudio

3.12.0-alpha

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    TriageIssue needs to be assessed and labeled, further information on reported might be needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions