Solutions for vtkCollection API issues

But the instance is then mutable!

I think the fundamental problem with any wrapper code is that no other languages, AFAIK, support passing const reference parameters.

If you got a vtkHomogeneousTransform*, sure. If you got const vtkHomogeneousTransform*, then you also need const_cast (with its various caveats and footnotes about its use).

The problem isn’t mutability. It’s mutability as the wrong type. A vtkPerspectiveTransform has limits on what it can represent; using it as the more general vtkHomogeneousTransform is just asking to violate those invariants.

Consider this:

class Ellipse {
    double minor;
    double major;
public:
    Ellipse(double major, double minor) : major(major), minor(minor) {}
};

class Circle : public Ellipse {
public:
    Circle(double r) : Ellipse(r, r) {}
    void set_radius(double r) { major = minor = r; }
};

If Ellipse::set_minor() existed, Circle::set_minor() could be called. This…does not work well unless Ellipse knows to also update major at the same time. However, if I have Ellipse* e, dynamic_cast<Circle*>(e)->set_radius(0.1) is just fine (modulo nullptr checks).

We seem to be talking at cross purposes.

I thought the issue was having add/insert methods defined too low in the class hierarchy which allowed base types into the collection. But surely this can be handled via templating. So the only other issue is when you want to only permit iteration in some parts of the code. If the collection can be cast dynamically it becomes mutable.

Yes, but to an API that properly constrains it (or you get nullptr back on the cast to an improper type). If you get a const vtkXCollection*, no amount of casting (other than the “I’m asking for trouble const_cast” case) will get you a mutable collection.

How do you propose this should be catered for in wrapper code?

Indeed, wrappers do have decisions to make. With Python, shimming in vtkFoo_const classes probably isn’t too hard, just probably very confusing if/when backtraces end up happening. You also have this problem:

class vtkFoo_const(vtkBase):
    pass # const methods

class vtkFoo_nonconst(vtkFoo_const):
    pass # add in non-const methods

class vtkBar_const(vtkFoo_const):
    pass

# How to do this? Mixins? But then how to make a "real" vtkFoo?
class vtkBar_nonconst(vtkBar_const, vtkFoo_nonconst):
    pass

Alas, wrapping will always have some mismatches that occur. Python could probably get away with something like a metaclass like vtkConst(vtkFoo) that wraps member access as vtkConst() objects and throws on any mutable method call (const-overloads would need some assistance here).

I don’t know Java well enough to know how it’d work there.

Either way, I think if the C++ API isn’t sound, the wrappers have zero chance of getting it right. If the wrappers have something they can’t handle, then it’s an API not accessible to them. I don’t think they should have absolute veto over something, but they certainly can influence it, but also not to the point of making the C++ API a ball of barbed wire.

You do this with interfaces in Java.

So in the class diagram below the vtkAbstractContainer class would become vtkContainerInterface implemented by both vtkContainer (non-const) and vtkContainerDecorator (const). Then rinse and repeat inheriting from the const and non-const classes and introducing new interfaces.