API: Major refactoring of all codes to use a strict Interface/AbstractBaseClass, Concrete style of naming#1537
Conversation
b43ee15 to
5328ed3
Compare
Phase 1: Add Code_Review/Phase1_Interface_Review.md documenting existing interface class usage across the codebase. Phase 2: Extract ISegmentFeatures interface from SegmentFeatures and rename to AbstractSegmentFeatures. ISegmentFeatures defines the pure virtual contract (execute, getSeed, determineGrouping, randomizeFeatureIds, initializeStaticVoxelSeedGenerator) along with SeedGenerator, NeighborScheme, and CompareFunctor types. AbstractSegmentFeatures provides the concrete implementations and inherits from ISegmentFeatures. All subclasses (ScalarSegmentFeatures, EBSDSegmentFeatures, CAxisSegmentFeatures) updated to inherit from AbstractSegmentFeatures. Also includes: IParallelAlgorithm renamed to ParallelAlgorithm, MaskCompare utilities updates, and various algorithm fixes from the develop branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> (cherry picked from commit 22e0b2d)
…r interface Transform IFilter from an abstract base class into a pure virtual interface. Create AbstractFilter to hold all concrete implementations (preflight, execute, toJson, fromJson, defaultTags, getDefaultArguments) and the template method pattern (preflightImpl/executeImpl). All ~300 concrete filters now inherit from AbstractFilter. Nested types (Message, MessageHandler, PreflightResult, ExecuteResult, etc.) remain on IFilter as the public API contract, minimizing changes to algorithm/utility code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add pure IPipelineNode interface defining the public contract for all pipeline nodes (identity, operations, state queries). Signal/observer mechanisms stay on AbstractPipelineNode since they use AbstractPipelineNode* in callbacks. NodeType enum and RenamedPath/RenamedPaths types move to the interface but remain accessible via inheritance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New files created: - src/simplnx/Plugin/IPlugin.hpp - Pure interface with all 11 public methods as pure virtual (getName, getDescription, getId, getVendor, containsFilterId, createFilter, getFilterHandles, getFilterCount, getDataIOManagers, getSimplToSimplnxMap, setOocTempDirectory), plus the 6 nested types (IdType, FilterContainerType, IOManagerPointer, IOManagersContainerType, SIMPLData, SIMPLMapType) - src/simplnx/Plugin/IPlugin.cpp - Destructor-only implementation Files modified: - src/simplnx/Plugin/AbstractPlugin.hpp - Now inherits from IPlugin, removed all type aliases and SIMPLData struct (moved to interface, still accessible via inheritance), added override to all 11 overriding methods - CMakeLists.txt - Added IPlugin.hpp to both header lists and IPlugin.cpp to sources - src/Plugins/SimplnxCore/wrapping/python/simplnxpy.cpp - Added IPlugin include, pybind11 binding, and registered AbstractPlugin as inheriting from IPlugin Files requiring NO changes: - All 8 plugin classes (SimplnxCore, OrientationAnalysis, ITK, TestOne, TestTwo, SimplnxReview, TestPlugin, PythonPlugin) - they inherit AbstractPlugin, no change needed - PluginLoader, FilterList, Application, Preferences - use AbstractPlugin* which still works - CreatePluginFunc/DestroyPluginFunc typedefs and SIMPLNX_DEF_PLUGIN_IMPL macro - stay on AbstractPlugin.hpp - All DREAM3DNX files - use AbstractPlugin*
…interfaces Renamed 6 geometry classes from I* to Abstract* prefix and extracted standalone pure virtual interfaces above them: - IGeometry → AbstractGeometry (+ new IGeometry interface) - IGridGeometry → AbstractGridGeometry (+ new IGridGeometry interface) - INodeGeometry0D-3D → AbstractNodeGeometry0D-3D (+ new interfaces) Interfaces are standalone (no inheritance hierarchy) to avoid diamond inheritance. Type aliases, enums, and string constants live on the interfaces so scope resolution (e.g. IGeometry::Type) works unchanged. k_TypeName strings preserved for serialization compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Created IDataStructure pure virtual interface with all non-template public methods from DataStructure. DataStructure now inherits from IDataStructure with override on all implemented methods. Template convenience methods (getDataAs, getDataRefAs, etc.) remain on DataStructure only since C++ templates cannot be virtual. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…diagram Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nterfaces Move concrete method implementations from IDataStore (getSize, size, empty, getDataFormat) and IListStore (size) down to their respective Abstract* template classes. All three interfaces now have only pure virtual methods. Updated class hierarchy diagram and review document. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move toJson(), fromJson(), construct() implementations and toJsonImpl()/fromJsonImpl() template-method hooks from IParameter down to AbstractParameter. IParameter is now a pure interface with 14 pure virtual methods and zero concrete methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ect hierarchy - Extract IArrayThreshold pure interface, rename old class to AbstractArrayThreshold - Refactor IJsonPipelineParser to pure interface, create AbstractJsonPipelineParser - Rename IArray -> AbstractArray (k_TypeName "IArray" preserved for serialization) - Rename IDataArray -> AbstractDataArray (k_TypeName "IDataArray" preserved) - Rename INeighborList -> AbstractNeighborList (k_TypeName "INeighborList" preserved) - Update DataObject::Type enum members (numeric values unchanged) - Update Phase_8_Interface_Review.md and Phase_8_Class_Hierarchy.dot - All 1106/1107 tests pass (1 expected failure: ExecuteProcessFilter) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… remove dead JSON code Phase 12: Renamed IDataIOManager to AbstractDataIOManager across 14 source files and CMakeLists.txt. The class has 3 data members, ~9 concrete methods, and only 1 pure virtual (formatName()), so it is an abstract base class, not a pure interface. Phase 13: Removed the dead src/simplnx/Utilities/Parsing/JSON/ directory (15 files) which was not compiled (absent from CMakeLists.txt) and not referenced from any compiled source. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lasses Add IArray, IDataArray, and INeighborList as aliases for AbstractArray, AbstractDataArray, and AbstractNeighborList in the Python bindings so existing scripts continue to work. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…o AbstractDataObject Extract IDataObject pure interface (21 pure virtual methods) from DataObject, rename DataObject to AbstractDataObject, update all references across simplnx, external plugins (FileStore, Synthetic), and DREAM3DNX. Add backwards-compatible Python alias for DataObject. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5328ed3 to
284b797
Compare
Phase 15 Summary: Fix I-Prefix Naming ViolationsPhase 15 completes the COM-style naming convention cleanup. All Part A: Renamed 8 I-prefixed non-interfaces → Abstract*These classes used the
Part B: Promoted AbstractStringStore → IStringStore
Applied Across Repositories
Post-Rebase FixesAfter rebasing onto the latest develop branches, several conflicts were resolved:
Test Resultssimplnx-Rel: 1060/1062 pass
Documentation Updated
Current Interface Count22 pure interfaces (up from 21 after Phase 14): |
284b797 to
b19af99
Compare
Complete Summary of Name Changes in This BranchThis branch enforces a strict COM-style naming convention across the entire codebase:
Classes Renamed (Old → New)Phase 2
Phase 3
A new pure interface Phase 6
New pure interfaces Phase 10
Phase 11
New pure interfaces Phase 12
Phase 14
A new pure interface Phase 15
New Pure Interfaces CreatedThese are brand-new classes extracted during the refactoring:
Python Backwards-Compatible AliasesThe following aliases were added to the Python bindings so existing scripts continue to work: simplnx.DataObject → simplnx.AbstractDataObject
simplnx.IArray → simplnx.AbstractArray
simplnx.IDataArray → simplnx.AbstractDataArray
simplnx.INeighborList → simplnx.AbstractNeighborList
simplnx.IDataCreationAction → simplnx.AbstractDataCreationActionDead Code Removed
Serialization CompatibilityAll renamed geometry classes preserve their original |
…mote AbstractStringStore to IStringStore Part A: Renamed 8 misnamed I-prefixed classes to Abstract*: - IDataCreationAction → AbstractDataCreationAction (Output.hpp + 31 files) - IDataIO → AbstractDataIO (file rename + class rename) - IGeometryIO → AbstractGeometryIO (file rename + class rename) - IGridGeometryIO → AbstractGridGeometryIO (file rename + class rename) - INodeGeom0dIO → AbstractNodeGeom0dIO (file rename + class rename) - INodeGeom1dIO → AbstractNodeGeom1dIO (file rename + class rename) - INodeGeom2dIO → AbstractNodeGeom2dIO (file rename + class rename) - INodeGeom3dIO → AbstractNodeGeom3dIO (file rename + class rename) Part B: Renamed AbstractStringStore → IStringStore (no data members, pure interface) Applied same renames to FileStore plugin (Zarr IO equivalents). Added backwards-compatible Python alias for IDataCreationAction. All 1106/1107 tests pass (1 expected failure: ExecuteProcessFilter). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b19af99 to
b741182
Compare
Comments from the requestorAgain the objective is to have a better separation between the host projects and the plugins. It is also to allow using a plugin built independently from any host application and most often after the host application. You could make a new plugin and send it to another team without having to provide a new host application. So we would use explicit linkage in the host using def files to alias the exports. We cannot rely on the decorated names because the name mangling is compiler dependent. An interface looks like: struct PrgniFace0
{
virtual void func0() = 0;
virtual int64_t func1(int v0, int v1) = 0;
virtual double func2(double v0) = 0;
};This would be in an header file shared by both host projects and plugins. To be clear, the interface header file is shared not exported. It is included in all projects supporting this interface. The interface is clean from any proprietary types and thus can be included in projects without having to pull any other proprietary includes. A plugin can support multiple interfaces. The plugin then implement a regular class derived from that interface and any other interfaces it needs to support. That class is not exported either. This class implements all the functions from the interface. This implementation class can also have data members. It can also handles any types, proprietary or not. The plugin then provides a class factory. This is the only export of the plugin which would look something like this: Header file: #pragma once
#include <prgniface0.h> // supported interface
#include "prgnpi0_global.h" // dll export definition
extern PRGNPI0_EXPORT std::shared_ptr<PrgniFace0> GetPrgniFace();This header file is not shared with host applications but the class factory is exported, PRGNPI0_EXPORT. Host applications rely on the export symbol to be the same in every plugins. Source file: #include <memory>
#include <qdebug.h>
#include "FilterFactory.h"
#include "Prgnpi0.h"
std::shared_ptr<PrgnPI0> pFilter;
std::shared_ptr<PrgniFace0> GetPrgniFace()
{
if (pFilter == nullptr)
{
pFilter = std::make_shared<PrgnPI0>();
}
std::shared_ptr<PrgniFace0> p = std::dynamic_pointer_cast<PrgniFace0, PrgnPI0>(pFilter);
return p;
}The “std::dynamic_pointer_cast” will not only upcast to the parent interface class, it will also properly handle the reference count. Using the plugin in a host application look like this: QLibrary lib;
QString path = "prgnpi0.dll";
qApp->addLibraryPath(".");
lib.setFileName(path);
lib.load(); // Loads the library
if (lib.isLoaded())
{
qDebug() << "\nLibrary " << path.toStdString().c_str() << " loaded\n";
}
else
{
qDebug() << "\nError " << lib.errorString() << "\n";
}
typedef std::shared_ptr<PrgniFace0>(__cdecl* GetiFace)();
GetiFace pGetPrgniFace = reinterpret_cast<GetiFace>(lib.resolve("GetPrgniFace")); // gets the class factory
if (pGetPrgniFace)
{
std::shared_ptr<PrgniFace0> p = pGetPrgniFace(); // invoke class factory to get the interface
if (p)
{
qDebug() << "Found interface PrgniFace0\n";
// use the interface
p->func0();
int iVal = p->func1(2, 5);
double dVal = p->func2(2);
qDebug() << iVal << " ," << dVal << "\n";
}
else
{
qDebug() << "Cannot find interface PrgniFace0\n";
}
}
else
{
qDebug() << "\nError: Unable to resolve GetPrgniFace0\n";
}Thia scheme is loosely based on the Component Object Model, COM, from Microsoft. |
|
Analysis of the requestor's code What it describes The section lays out a COM-inspired plugin architecture where:
Strengths of the description
Things worth noting / potential concerns
Relationship to the refactoring work already done Looking at the memory notes (Phases 1–15), the project has already done extensive work extracting pure interfaces (IFilter, Summary The architecture is sound for the stated goal — it's a proven pattern (essentially COM-lite). The main gaps in the description |
Major Refactoring of the SIMPLNX code base
The point of this PR is to create pure virtual Interfaces, Abstract classes and concreate classes where needed. Extract pure virtual where needed and create the intermediate abstract clases where needed. The entire point of this exercise is to create a clean COM style of interface for outsiders to use.