Files
ResInsight/docs/pdm-nested-collection-design.md
T
Magne Sjaastad 92da6633bb #13987 PDM: Add caf::PdmNestedCollection framework
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.
2026-05-13 14:53:00 +02:00

3.3 KiB
Raw Blame History

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:

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 accessorsm_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

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 810 derived classes that's ~200250 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 12 — 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.