mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
Introduce a shared base for tree-shaped folder containers that hold a list of leaf items together with a list of subcontainers of the same kind. Replaces the per-class plumbing (subcollections field, top-folder flag, add/remove/find sub-collection, factory) that is duplicated across RimSurfaceCollection, RimPolygonCollection and similar candidates. - caf::PdmNestedCollectionBase: non-templated PdmObject base exposing collection-name, add-new-subcollection, leaf-check and topmost-folder marking for generic command features and script methods. - caf::PdmNestedCollection<SelfT, ItemT>: CRTP template that owns the typed m_subCollections field and inherits the m_items array from PdmObjectCollection<ItemT>. Provides allItems(), find-sub-collection, and a typed factory hook. - RimNestedMirrorCollectionInView: view-side analogue for in-view collections that mirror a nested source collection, with orphan-sweep + find-or-create + reorder sync. Design rationale (templated vs non-templated, break-even on derived class count) lives in docs/pdm-nested-collection-design.md. No callers migrated in this commit.
82 lines
3.3 KiB
Markdown
82 lines
3.3 KiB
Markdown
# `caf::PdmNestedCollection` — design notes
|
||
|
||
## Context
|
||
|
||
`caf::PdmNestedCollection` is the base class for tree-shaped PDM containers — a
|
||
folder of items that can also contain folders of the same kind. The design is a
|
||
non-template concrete base + a CRTP template:
|
||
|
||
```cpp
|
||
class PdmNestedCollectionBase : public PdmObject
|
||
{
|
||
QString collectionName() const;
|
||
void setCollectionName( const QString& name );
|
||
virtual bool canAddSubCollection() const;
|
||
virtual PdmObject* addNewSubCollection();
|
||
void setAsTopmostFolder();
|
||
PdmField<QString> m_collectionName;
|
||
};
|
||
|
||
template <typename SelfT, typename ItemT>
|
||
class PdmNestedCollection : public PdmNestedCollectionBase
|
||
{
|
||
// typed accessors + storage for items and subcollections
|
||
PdmChildArrayField<SelfT*> m_subCollections;
|
||
PdmChildArrayField<ItemT*> m_items;
|
||
};
|
||
```
|
||
|
||
`PdmNestedCollectionBase` is the shared PdmObject ancestor — the anchor point
|
||
where the CAF method factory can register generic script methods (like
|
||
`AddFolder`) once and have them surface on every derived nested collection via
|
||
the inheritance walk and Python class hierarchy.
|
||
|
||
`PdmNestedCollection<SelfT, ItemT>` adds typed storage and accessors. `SelfT` is
|
||
the CRTP self type (so subcollections are typed `SelfT*`); `ItemT` is the leaf
|
||
item type. The template does not inherit `PdmObjectCollection<ItemT>` — m_items
|
||
is declared here directly, so the inheritance chain stays linear and free of
|
||
diamond shapes.
|
||
|
||
## Why both a non-template base and a CRTP template
|
||
|
||
Two needs that don't fit into a single layer:
|
||
|
||
- **CAF method registration is keyed by `classKeyword`** — only a concrete
|
||
non-template class can serve as the anchor. Hence the non-template
|
||
`PdmNestedCollectionBase`. Without it, generic script methods (`AddFolder`)
|
||
would have to be re-registered per derived class.
|
||
|
||
- **Typed storage and accessors** — `m_subCollections : PdmChildArrayField<SelfT*>`
|
||
needs `SelfT`, which only exists as a template parameter. Hence the CRTP
|
||
template layer on top of the base.
|
||
|
||
## Alternative considered: non-templated base only
|
||
|
||
```cpp
|
||
class PdmNestedCollection : public PdmObject
|
||
{
|
||
...
|
||
virtual PdmObject* createSubCollection() const = 0;
|
||
// m_subCollectionsField / m_itemsField stored as type-erased handles
|
||
};
|
||
```
|
||
|
||
Each derived class would own its typed `PdmChildArrayField<DerivedType*>
|
||
m_subCollections` and `PdmChildArrayField<ItemType*> m_items`, register them
|
||
with the base in the constructor, and override `createSubCollection()`. About
|
||
~25 lines of boilerplate per derived class beyond what the current design needs:
|
||
two field declarations, two `registerXxxField` calls, an override of
|
||
`createSubCollection`, re-declared typed accessors.
|
||
|
||
At the projected 8–10 derived classes that's ~200–250 lines of repeated code.
|
||
The hybrid (non-template base + template) design pays the cost once in the
|
||
framework and gets typed access plus generic registration for free.
|
||
|
||
## When to revisit
|
||
|
||
- If the count of nested-collection types drops to 1–2 — the template tier no
|
||
longer earns its keep, and a pure non-template base wins on simplicity.
|
||
- If template instantiation cost shows up in build profiling for the PDM layer.
|
||
- If the typed accessors are rarely used in practice and most call sites
|
||
immediately cast to `PdmObject*` anyway.
|