You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #714 fixes undefined behavior caused by treating arbitrary SoPath objects as SoFullPath objects. It adds explicit full-path accessors to SoPath and migrates Coin's internal fake downcasts, while retaining SoFullPath for compatibility.
This issue is intentionally about a future ABI/API-breaking Coin 5 design, not a prerequisite for #714.
Historical model
SoFullPath comes from the Open Inventor API. It is declared as a public subclass of SoPath, adds no data members, has a private constructor/destructor, and exposes alternate non-virtual accessors that include hidden children. Coin's initial 1999 revision documented that an arbitrary SoPath could be cast to SoFullPath because the derived class had no additional data.
That made SoFullPath act as an "extended interface" or view selected by changing the static pointer type:
However, the November 1997 C++ working paper already specified in [expr.static.cast] that a base-to-derived pointer cast is only valid when the base pointer designates a base subobject of an actual derived object. Otherwise the result of the cast is undefined. C++98 and every later standard retain this object-identity requirement. Equal layout and the absence of additional data members do not create a derived object.
The inheritance itself has one valid use: SoTempPath genuinely derives from SoFullPath. The invalid part is using inheritance as a universal view over plain SoPath instances.
Why discuss this for Coin 5
Coin has explicitly required at least C++11 since version 4.0.3. PR #714 reconciles the current implementation with that language model without breaking API or ABI, but leaves the historical compatibility facade in place.
A major release gives us an opportunity to represent the original "extended interface" intention directly and safely.
Possible Coin 5 model
A modern view would use composition rather than a downcast:
SoPath * path = ...;
auto full = path->fullView();
SoNode * tail = full.getTail();
The view can be one pointer wide, require no allocation, and inline to the same underlying accesses. A const view and an explicit lifetime policy would be needed. We should decide whether the public view is borrowed, retains its SoPath through SoRef, or whether both forms are useful.
Relationship to Open Inventor compatibility
There are two different compatibility goals:
Literal C++ API compatibility. Open Inventor defines SoFullPath : public SoPath. Code may pass SoFullPath* where SoPath* is expected and may use all inherited SoPath operations. A composition-based view would break this source-level relationship as well as ABI.
Architectural intent. Open Inventor and Coin describe SoFullPath as an extended interface over storage already owned by SoPath. A view represents that intent more accurately than a false derived-class cast.
There is also a useful precedent in current Open Inventor managed bindings: they expose a SoPath.FullPath property and document usage such as path.FullPath.GetTail(), rather than asking managed-language users to perform the C++ downcast.
Design alternatives
A. Add SoFullPathView and retain legacy SoFullPath temporarily
Best migration path and clearest naming.
Preserves a compatibility window.
Leaves two full-path interfaces during the transition.
B. Redefine SoFullPath itself as the view in Coin 5
Makes the historic name finally match its documented "extended interface" purpose.
Breaks Open Inventor C++ source compatibility, not only ABI.
Requires changing SoTempPath to derive directly from SoPath.
Requires migration for code relying on implicit SoFullPath* to SoPath* conversion or inherited methods.
C. Keep the inheritance permanently, but retire its use as a view
Keep SoFullPath only for genuine derived objects and compatibility.
Make SoPath::getFull*() the normal API.
Smallest ecosystem disruption, but preserves a confusing historical type.
Questions for the Coin 5 design
Is literal Open Inventor C++ source compatibility still a Coin 5 requirement?
Should the modern type be named SoFullPathView, or should Coin 5 redefine SoFullPath?
Should a view be borrowed or retain the underlying SoPath?
Should SoNodeKitPath eventually become another view over the same storage?
How many release cycles should the legacy class/accessors remain deprecated before removal?
Should SoTempPath derive directly from SoPath in Coin 5?
Motivation
PR #714 fixes undefined behavior caused by treating arbitrary
SoPathobjects asSoFullPathobjects. It adds explicit full-path accessors toSoPathand migrates Coin's internal fake downcasts, while retainingSoFullPathfor compatibility.This issue is intentionally about a future ABI/API-breaking Coin 5 design, not a prerequisite for #714.
Historical model
SoFullPathcomes from the Open Inventor API. It is declared as a public subclass ofSoPath, adds no data members, has a private constructor/destructor, and exposes alternate non-virtual accessors that include hidden children. Coin's initial 1999 revision documented that an arbitrarySoPathcould be cast toSoFullPathbecause the derived class had no additional data.That made
SoFullPathact as an "extended interface" or view selected by changing the static pointer type:However, the November 1997 C++ working paper already specified in
[expr.static.cast]that a base-to-derived pointer cast is only valid when the base pointer designates a base subobject of an actual derived object. Otherwise the result of the cast is undefined. C++98 and every later standard retain this object-identity requirement. Equal layout and the absence of additional data members do not create a derived object.The inheritance itself has one valid use:
SoTempPathgenuinely derives fromSoFullPath. The invalid part is using inheritance as a universal view over plainSoPathinstances.Why discuss this for Coin 5
Coin has explicitly required at least C++11 since version 4.0.3. PR #714 reconciles the current implementation with that language model without breaking API or ABI, but leaves the historical compatibility facade in place.
A major release gives us an opportunity to represent the original "extended interface" intention directly and safely.
Possible Coin 5 model
A modern view would use composition rather than a downcast:
Usage becomes:
The view can be one pointer wide, require no allocation, and inline to the same underlying accesses. A const view and an explicit lifetime policy would be needed. We should decide whether the public view is borrowed, retains its
SoPaththroughSoRef, or whether both forms are useful.Relationship to Open Inventor compatibility
There are two different compatibility goals:
SoFullPath : public SoPath. Code may passSoFullPath*whereSoPath*is expected and may use all inheritedSoPathoperations. A composition-based view would break this source-level relationship as well as ABI.SoFullPathas an extended interface over storage already owned bySoPath. A view represents that intent more accurately than a false derived-class cast.There is also a useful precedent in current Open Inventor managed bindings: they expose a
SoPath.FullPathproperty and document usage such aspath.FullPath.GetTail(), rather than asking managed-language users to perform the C++ downcast.Design alternatives
A. Add
SoFullPathViewand retain legacySoFullPathtemporarilyB. Redefine
SoFullPathitself as the view in Coin 5SoTempPathto derive directly fromSoPath.SoFullPath*toSoPath*conversion or inherited methods.C. Keep the inheritance permanently, but retire its use as a view
SoFullPathonly for genuine derived objects and compatibility.SoPath::getFull*()the normal API.Questions for the Coin 5 design
SoFullPathView, or should Coin 5 redefineSoFullPath?SoPath?SoNodeKitPatheventually become another view over the same storage?SoTempPathderive directly fromSoPathin Coin 5?Suggested migration sequence
SoPath::getFull*()and remove Coin's invalid internal downcasts.SoPath*toSoFullPath*casts as unsupported and provide a downstream migration table.SoTempPath's base if necessary and choose between alternatives A, B, and C above.References
SoFullPathas an "extended interface": d4c9862[expr.static.cast]: https://open-std.org/jtc1/sc22/WG21/docs/wp/html/nov97-2/expr.htmlSoPath/SoFullPathdefinition: https://developer.openinventor.com/refmans/2026.1/RefManCpp/_so_path_8h_source.htmlSoPath.FullPathAPI: https://developer.openinventor.com/refmans/latest/RefManNet/html/e4f6fe7d-de3a-309f-550d-76b7a16ba764.htm