That’s all right.
There is definitely a use case for weak pointers. I have one at the model-view I am coding for a new TaskSketcherElements, where if the resource has been deleted I need to handle it differently (as it makes no sense to update the model with information about a geometry that no longer exists).
On the other side, having only the possibility to retrieve weak pointers from PropertyGeometryList may not be very convenient (or even efficient).
PropertyGeometryList stores internally a std::vector<std::shared_ptr>. This means that to provide weak pointers, one has to create a new array of type
std::vector<std::weak_ptr<Geometry>>
and populate it in a loop with the shared pointers (we do something similar for geometry extensions in Part::Geometry::getExtensions).
In many operations (using the legacy approach), the first thing to do is to lock the weak pointers to make the shared_ptrs, just to create a new list, modify some geometries (by making clones of the std::shared_ptrs first), and then modify the property by transferring the ownership of the vector of shared_ptrs back to PropertyGeometryList. Which idiomatically should be something like (transfer ownership of a vector of smart shared pointers):
void setValues(std::vector<std::shared_ptr<Geometry>> &&);
These operations I am referring to guarantee that the lifetime of no pointer will be deleted during it (they grab the property, modify it creating new pointers where necessary, and they set the property with the modified version). So perhaps it would make sense to have some function similar to this one:
const std::vector<std::shared_ptr<Geometry>> &getValues() const {
return _lValueList;
}
in addition to one like this:
const std::vector<std::weak_ptr<Geometry>> getValuesAsWeakPtr() const
Alternatively (or in addition), I am considering providing a different interface for these operations:
geometry.setValuesSequentially(int index, std::shared_ptr<Geometry> && newgeometry pointer)
editlock<geometry>; // blocks hasSetValue until it goes out of scope
geometry.setValuesSequentially(index1, newgeometrypointer);
geometry.setValuesSequentially(index2, newgeometrypointer);
This may make the property way easier to use in client code, as it avoids having to create a new list and track which pointers to directly copy and which ones to clone and then assigning the whole list even if often many pointers are unchanged…
Changes to PropertyGeometryList usually also call for changes to:
- Part::Geometry::clone() and Part::Geometry::copy() so that they return smart pointers too.
- GeometryFacade (to take a smart pointer instead of a C pointer)
- GeomList and GeomListFacade (to take a smart pointer instead of a C pointer)
- About to all methods in SketchObject.
- Sketcher::Sketch (the solver interface).
These are so core that they recursively extend to almost every other class in Sketcher WB, making it a huge refactor.
I have started two refactors to throw away just to see the problems and try to get a better solution.
That’s why I am also considering to keep different interfaces, including a regular pointer interface, at least temporarily, to support legacy code and split changes into different phases. Then when everything is ported, the C pointer interfaces may be removed for good.
Of course, I also see the risks associated to different interfaces and how they may be abused (for example for retaining a copy of a pointer and preventing it from being released).
All this are just ideas trying to find the way ™.
I will now have over one week to think about it. Then when I am back hopefully I will have a clearer mind. Of course, if you see further, leave a comment here and I take it as input.