Skip to content

Make QuantLib::ext::shared_ptr resolvable by SWIG (#351) - #353

Open
dazivo wants to merge 1 commit into
OpenSourceRisk:masterfrom
dazivo:fix/351-swig-quantlib-ext-shared-ptr-alias
Open

Make QuantLib::ext::shared_ptr resolvable by SWIG (#351)#353
dazivo wants to merge 1 commit into
OpenSourceRisk:masterfrom
dazivo:fix/351-swig-quantlib-ext-shared-ptr-alias

Conversation

@dazivo

@dazivo dazivo commented Aug 18, 2026

Copy link
Copy Markdown

Fixes #351.

QuantLib-SWIG's common.i sets SWIG_SHARED_PTR_NAMESPACE to ext, and the C++ alias namespace ext = QuantLib::ext it depends on sits inside a %{ %} block that the SWIG parser never sees. Declarations spelled QuantLib::ext::shared_ptr<T> were therefore registered as a type distinct from ext::shared_ptr<T> and wrapped as an opaque pointer rather than a proper proxy.

The two spellings are mixed throughout the interface files, so a value produced by one could not be passed to the other. In particular OREApp::getCube() (unqualified) could not be handed to InputParameters::setCube() (qualified), which made in-memory cube reuse unreachable from Python:

TypeError: in method 'InputParameters_setCube', argument 2 of type
           'QuantLib::ext::shared_ptr< ore::analytics::NPVCube > const &'

setMarketCube/getMarketCube were affected identically, along with nine further declarations in orea_app.i alone -- setXvaSensiPricingEngine and its three siblings reject a freshly constructed EngineData on the released 1.8.16.0 wheel, while 1.8.15.0 (where both sides read ext::shared_ptr) accepts every one of them.

Declare the namespace alias where SWIG can see it, collapsing both spellings onto the same wrapped type. Regenerating oreanalytics.i shows all 80 opaque SWIGTYPE_p_QuantLib__ext__shared_ptrT_* descriptors resolve to their registered ext::shared_ptr counterparts, with no Python symbols added or removed; the Python API surface diff is docstring-only.

Verified by building the bindings and running them in a debian trixie container (clang 19, swig 4.3, apt boost): a freshly constructed in-memory cube passes InputParameters::setCube, all four EngineData setters accept a wrapped EngineData, and mismatched argument types are still rejected.

Reviving the qualified overloads does surface new SWIG warnings (53 lines, all Warning 509 overload shadowing; 88 -> 140 in total with SWIG 4.4.1), confined to three constructors: DigitalCMSLegData and DigitalCMSSpreadLegData taking a shared_ptr to their underlying leg data, and BasketData taking a vector of shared_ptr constituents. Those constructors were dead under the qualified spelling and %extend value-copy workarounds had been added alongside them; this change brings the native constructors back to life, and they now shadow the workarounds. As a consequence, passing a CMSLegData to DigitalCMSLegData now shares the underlying object with the constructed leg data, as the C++ API does, instead of copying it through the workaround.

The accompanying test drives the EngineData setters and the setCube round-trip; the round-trip constructs its cube directly and skips on builds where the in-memory cubes are still wrongly abstract (#354, fixed by #355). It fails against the 1.8.16.0 wheel.

QuantLib-SWIG's common.i sets SWIG_SHARED_PTR_NAMESPACE to `ext`, and the
C++ alias `namespace ext = QuantLib::ext` it depends on sits inside a %{ %}
block that the SWIG parser never sees. Declarations spelled
`QuantLib::ext::shared_ptr<T>` were therefore registered as a type distinct
from `ext::shared_ptr<T>` and wrapped as an opaque pointer rather than a
proper proxy.

The two spellings are mixed throughout the interface files, so a value
produced by one could not be passed to the other. In particular
OREApp::getCube() (unqualified) could not be handed to
InputParameters::setCube() (qualified), which made in-memory cube reuse
unreachable from Python:

    TypeError: in method 'InputParameters_setCube', argument 2 of type
               'QuantLib::ext::shared_ptr< ore::analytics::NPVCube > const &'

setMarketCube/getMarketCube were affected identically, along with nine
further declarations in orea_app.i alone -- setXvaSensiPricingEngine and
its three siblings reject a freshly constructed EngineData on the released
1.8.16.0 wheel, while 1.8.15.0 (where both sides read `ext::shared_ptr`)
accepts every one of them.

Declare the namespace alias where SWIG can see it, collapsing both
spellings onto the same wrapped type. Regenerating oreanalytics.i shows
all 80 opaque SWIGTYPE_p_QuantLib__ext__shared_ptrT_* descriptors resolve
to their registered ext::shared_ptr counterparts, with no Python symbols
added or removed; the Python API surface diff is docstring-only.

Verified by building the bindings and running them in a debian trixie
container (clang 19, swig 4.3, apt boost): a freshly constructed
in-memory cube passes InputParameters::setCube, all four EngineData
setters accept a wrapped EngineData, and mismatched argument types are
still rejected.

Reviving the qualified overloads does surface new SWIG warnings (53 lines,
all Warning 509 overload shadowing; 88 -> 140 in total with SWIG 4.4.1),
confined to three constructors: DigitalCMSLegData and
DigitalCMSSpreadLegData taking a shared_ptr to their underlying leg data,
and BasketData taking a vector of shared_ptr constituents. Those
constructors were dead under the qualified spelling and %extend value-copy
workarounds had been added alongside them; this change brings the native
constructors back to life, and they now shadow the workarounds. As a
consequence, passing a CMSLegData to DigitalCMSLegData now shares the
underlying object with the constructed leg data, as the C++ API does,
instead of copying it through the workaround.

The accompanying test drives the EngineData setters and the setCube
round-trip; the round-trip constructs its cube directly and skips on
builds where the in-memory cubes are still wrongly abstract (issue OpenSourceRisk#354).
It fails against the 1.8.16.0 wheel.
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

Successfully merging this pull request may close these issues.

Python bindings: getCube/setCube declare different shared_ptr spellings, so in-memory cube reuse raises TypeError (regression in 1.8.16.0)

1 participant