From b64aab42a2d6dcc87957a50d22f139fd67ce6998 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 22 Jun 2026 17:08:34 +0200 Subject: [PATCH 01/71] Start refactoring of ObjectFactory --- .../src/sofa/component/statecontainer/init.h | 5 - Sofa/framework/Core/CMakeLists.txt | 5 +- .../Core/src/sofa/core/CategoryLibrary.cpp | 468 +++++----- .../Core/src/sofa/core/CategoryLibrary.h | 166 ++-- .../Core/src/sofa/core/ComponentCreator.h | 58 ++ .../Core/src/sofa/core/ComponentFactory.cpp | 269 ++++++ .../Core/src/sofa/core/ComponentFactory.h | 187 ++++ .../Core/src/sofa/core/ComponentLibrary.cpp | 220 ++--- .../Core/src/sofa/core/ComponentLibrary.h | 126 +-- .../src/sofa/core/ComponentRegistrationData.h | 167 ++++ .../Core/src/sofa/core/ObjectFactory.cpp | 855 ------------------ .../Core/src/sofa/core/ObjectFactory.h | 401 +------- .../Core/src/sofa/core/ObjectFactoryJson.cpp | 314 +++---- .../Core/src/sofa/core/ObjectFactoryJson.h | 10 +- .../Core/src/sofa/core/SofaLibrary.cpp | 306 +++---- .../Core/src/sofa/core/SofaLibrary.h | 140 +-- .../sofa/simulation/DefaultAnimationLoop.cpp | 2 +- .../SceneCheckMissingRequiredPlugin.cpp | 52 +- .../SceneChecking/SceneCheckUsingAlias.cpp | 14 +- 19 files changed, 1597 insertions(+), 2168 deletions(-) create mode 100644 Sofa/framework/Core/src/sofa/core/ComponentCreator.h create mode 100644 Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp create mode 100644 Sofa/framework/Core/src/sofa/core/ComponentFactory.h create mode 100644 Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h delete mode 100644 Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/init.h b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/init.h index 8beb96cc0d7..10aed6d49f2 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/init.h +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/init.h @@ -23,11 +23,6 @@ #include -namespace sofa::core -{ - class ObjectFactory; -} - namespace sofa::component::statecontainer { SOFA_COMPONENT_STATECONTAINER_API void init(); diff --git a/Sofa/framework/Core/CMakeLists.txt b/Sofa/framework/Core/CMakeLists.txt index 5d049ec19bf..33881334318 100644 --- a/Sofa/framework/Core/CMakeLists.txt +++ b/Sofa/framework/Core/CMakeLists.txt @@ -31,8 +31,11 @@ set(HEADER_FILES ${SRC_ROOT}/CategoryLibrary.h ${SRC_ROOT}/CollisionElement.h ${SRC_ROOT}/CollisionModel.h + ${SRC_ROOT}/ComponentCreator.h + ${SRC_ROOT}/ComponentFactory.h ${SRC_ROOT}/ComponentLibrary.h ${SRC_ROOT}/ComponentNameHelper.h + ${SRC_ROOT}/ComponentRegistrationData.h ${SRC_ROOT}/ConstraintOrder.h ${SRC_ROOT}/ConstraintParams.h ${SRC_ROOT}/DataEngine.h @@ -232,6 +235,7 @@ set(SOURCE_FILES ${SRC_ROOT}/BehaviorModel.cpp ${SRC_ROOT}/CategoryLibrary.cpp ${SRC_ROOT}/CollisionModel.cpp + ${SRC_ROOT}/ComponentFactory.cpp ${SRC_ROOT}/ComponentLibrary.cpp ${SRC_ROOT}/ComponentNameHelper.cpp ${SRC_ROOT}/ConstraintParams.cpp @@ -248,7 +252,6 @@ set(SOURCE_FILES ${SRC_ROOT}/MechanicalParams.cpp ${SRC_ROOT}/Multi2Mapping.cpp ${SRC_ROOT}/MultiMapping.cpp - ${SRC_ROOT}/ObjectFactory.cpp ${SRC_ROOT}/ObjectFactoryJson.cpp ${SRC_ROOT}/PathResolver.cpp ${SRC_ROOT}/SofaLibrary.cpp diff --git a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp index 63ca8745277..f84a638cc97 100644 --- a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp +++ b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp @@ -1,234 +1,234 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sofa::core -{ - - -//------------------------------------------------------------------------------------------------------- -CategoryLibrary::CategoryLibrary( const std::string &categoryName): name(categoryName) -{ -} - - -ComponentLibrary *CategoryLibrary::addComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) -{ - //Special case of Mapping and MechanicalMapping - const bool isMechanicalMapping = (name == "MechanicalMapping"); - const bool isMapping = (name == "Mapping"); - - ComponentLibrary* component = createComponent(componentName, entry, exampleFiles); - - //Add the corresponding templates - std::map::iterator itTemplate; - - //It exists Mappings only Mechanical or only Visual. So, we must add the component if only a creator is available for the current category - bool componentCreationPossible=false; - //read all the template possible, and remove unused (for Mapping processing) - std::list templates; - for (itTemplate=entry->creatorMap.begin(); itTemplate!= entry->creatorMap.end(); ++itTemplate) - { - const std::string &templateName = itTemplate->first; - //If the component corresponds to a MechanicalMapping, we must remove the template related to the visual mapping - if (isMechanicalMapping) - { - const std::string nonMechanical = templateName.substr(0,7); - if (nonMechanical == "Mapping") continue; - } - //If the component corresponds to a Mapping, we must remove the template related to the Mechanical Mapping - else if (isMapping) - { - const std::string mechanical = templateName.substr(0,17); - if (mechanical == "MechanicalMapping") continue; - } - componentCreationPossible=true; - //component->addTemplate(itTemplate->first); - if (templateName == (entry->defaultTemplate.empty() ? std::string("Vec3d") : entry->defaultTemplate)) - templates.push_front(templateName); // make sure the default template is first - else - templates.push_back(templateName); - } - for (std::list::const_iterator it = templates.begin(); it != templates.end(); ++it) - component->addTemplate(*it); - component->endConstruction(); - - //If no constructor is available, we delete the component - if (!componentCreationPossible) - { - delete component; - component=nullptr; - } - else - components.push_back(component); - - return component; -} - -void CategoryLibrary::endConstruction() -{ -} - - -const ComponentLibrary *CategoryLibrary::getComponent( const std::string &categoryName) const -{ - for (VecComponentIterator it=components.begin(); it != components.end(); ++it) - { - if ((*it)->getName().find(categoryName) != std::string::npos) - return *it; - } - return nullptr; -} - - -std::vector CategoryLibrary::getCategories() -{ - return std::vector{ - "AnimationLoop", - "BehaviorModel", - "CollisionAlgorithm", - "CollisionModel", - "ConfigurationSetting", - "ConstraintSet", - "ConstraintSolver", - "ConstraintSolver", - "ContextObject", - "Controller", - "Engine", - "Exporter", - "ForceField", - "InteractionForceField", - "LinearSolver", - "LinearSystem", - "Loader", - "Mapping", - "Mass", - "MechanicalState", - "OdeSolver", - "OrderingMethod", - "ProjectiveConstraintSet", - "TopologicalMapping", - "Topology", - "TopologyObject", - "VisualModel", - "_Miscellaneous" - }; -} - -void CategoryLibrary::getCategories(const objectmodel::BaseClass* mclass, - std::vector& v) -{ - if (mclass->hasParent(objectmodel::ContextObject::GetClass())) - v.push_back("ContextObject"); - if (mclass->hasParent(visual::VisualModel::GetClass())) - v.push_back("VisualModel"); - if (mclass->hasParent(BehaviorModel::GetClass())) - v.push_back("BehaviorModel"); - if (mclass->hasParent(CollisionModel::GetClass())) - v.push_back("CollisionModel"); - if (mclass->hasParent(behavior::BaseMechanicalState::GetClass())) - v.push_back("MechanicalState"); - // A Mass is a technically a ForceField, but we don't want it to appear in the ForceField category - if (mclass->hasParent(behavior::BaseForceField::GetClass()) && !mclass->hasParent(behavior::BaseMass::GetClass())) - v.push_back("ForceField"); - if (mclass->hasParent(behavior::BaseInteractionForceField::GetClass())) - v.push_back("InteractionForceField"); - if (mclass->hasParent(behavior::BaseProjectiveConstraintSet::GetClass())) - v.push_back("ProjectiveConstraintSet"); - if (mclass->hasParent(behavior::BaseConstraintSet::GetClass())) - v.push_back("ConstraintSet"); - if (mclass->hasParent(BaseMapping::GetClass())) - v.push_back("Mapping"); - if (mclass->hasParent(DataEngine::GetClass())) - v.push_back("Engine"); - if (mclass->hasParent("BaseSimulationExporter")) - v.push_back("Exporter"); - if (mclass->hasParent(topology::TopologicalMapping::GetClass())) - v.push_back("TopologicalMapping"); - if (mclass->hasParent(behavior::BaseMass::GetClass())) - v.push_back("Mass"); - if (mclass->hasParent(behavior::OdeSolver::GetClass())) - v.push_back("OdeSolver"); - if (mclass->hasParent(behavior::ConstraintSolver::GetClass())) - v.push_back("ConstraintSolver"); - if (mclass->hasParent(behavior::BaseConstraintCorrection::GetClass())) - v.push_back("ConstraintSolver"); - if (mclass->hasParent(behavior::BaseMatrixLinearSystem::GetClass())) - v.push_back("LinearSystem"); - if (mclass->hasParent(behavior::BaseOrderingMethod::GetClass())) - v.push_back("OrderingMethod"); - if (mclass->hasParent(behavior::LinearSolver::GetClass())) - v.push_back("LinearSolver"); - if (mclass->hasParent(behavior::BaseAnimationLoop::GetClass())) - v.push_back("AnimationLoop"); - // Just like Mass and ForceField, we don't want TopologyObject to appear in the Topology category - if (mclass->hasParent(topology::Topology::GetClass()) && !mclass->hasParent(topology::BaseTopologyObject::GetClass())) - v.push_back("Topology"); - if (mclass->hasParent(topology::BaseTopologyObject::GetClass())) - v.push_back("TopologyObject"); - if (mclass->hasParent(behavior::BaseController::GetClass())) - v.push_back("Controller"); - if (mclass->hasParent(loader::BaseLoader::GetClass())) - v.push_back("Loader"); - if (mclass->hasParent(collision::CollisionAlgorithm::GetClass())) - v.push_back("CollisionAlgorithm"); - if (mclass->hasParent(collision::Pipeline::GetClass())) - v.push_back("CollisionAlgorithm"); - if (mclass->hasParent(collision::Intersection::GetClass())) - v.push_back("CollisionAlgorithm"); - if (mclass->hasParent(objectmodel::ConfigurationSetting::GetClass())) - v.push_back("ConfigurationSetting"); - if (v.empty()) - v.push_back("_Miscellaneous"); -} - - -} // namespace sofa::core +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// +// namespace sofa::core +// { +// +// +// //------------------------------------------------------------------------------------------------------- +// CategoryLibrary::CategoryLibrary( const std::string &categoryName): name(categoryName) +// { +// } +// +// +// ComponentLibrary *CategoryLibrary::addComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) +// { +// //Special case of Mapping and MechanicalMapping +// const bool isMechanicalMapping = (name == "MechanicalMapping"); +// const bool isMapping = (name == "Mapping"); +// +// ComponentLibrary* component = createComponent(componentName, entry, exampleFiles); +// +// //Add the corresponding templates +// std::map::iterator itTemplate; +// +// //It exists Mappings only Mechanical or only Visual. So, we must add the component if only a creator is available for the current category +// bool componentCreationPossible=false; +// //read all the template possible, and remove unused (for Mapping processing) +// std::list templates; +// for (itTemplate=entry->creatorMap.begin(); itTemplate!= entry->creatorMap.end(); ++itTemplate) +// { +// const std::string &templateName = itTemplate->first; +// //If the component corresponds to a MechanicalMapping, we must remove the template related to the visual mapping +// if (isMechanicalMapping) +// { +// const std::string nonMechanical = templateName.substr(0,7); +// if (nonMechanical == "Mapping") continue; +// } +// //If the component corresponds to a Mapping, we must remove the template related to the Mechanical Mapping +// else if (isMapping) +// { +// const std::string mechanical = templateName.substr(0,17); +// if (mechanical == "MechanicalMapping") continue; +// } +// componentCreationPossible=true; +// //component->addTemplate(itTemplate->first); +// if (templateName == (entry->defaultTemplate.empty() ? std::string("Vec3d") : entry->defaultTemplate)) +// templates.push_front(templateName); // make sure the default template is first +// else +// templates.push_back(templateName); +// } +// for (std::list::const_iterator it = templates.begin(); it != templates.end(); ++it) +// component->addTemplate(*it); +// component->endConstruction(); +// +// //If no constructor is available, we delete the component +// if (!componentCreationPossible) +// { +// delete component; +// component=nullptr; +// } +// else +// components.push_back(component); +// +// return component; +// } +// +// void CategoryLibrary::endConstruction() +// { +// } +// +// +// const ComponentLibrary *CategoryLibrary::getComponent( const std::string &categoryName) const +// { +// for (VecComponentIterator it=components.begin(); it != components.end(); ++it) +// { +// if ((*it)->getName().find(categoryName) != std::string::npos) +// return *it; +// } +// return nullptr; +// } +// +// +// std::vector CategoryLibrary::getCategories() +// { +// return std::vector{ +// "AnimationLoop", +// "BehaviorModel", +// "CollisionAlgorithm", +// "CollisionModel", +// "ConfigurationSetting", +// "ConstraintSet", +// "ConstraintSolver", +// "ConstraintSolver", +// "ContextObject", +// "Controller", +// "Engine", +// "Exporter", +// "ForceField", +// "InteractionForceField", +// "LinearSolver", +// "LinearSystem", +// "Loader", +// "Mapping", +// "Mass", +// "MechanicalState", +// "OdeSolver", +// "OrderingMethod", +// "ProjectiveConstraintSet", +// "TopologicalMapping", +// "Topology", +// "TopologyObject", +// "VisualModel", +// "_Miscellaneous" +// }; +// } +// +// void CategoryLibrary::getCategories(const objectmodel::BaseClass* mclass, +// std::vector& v) +// { +// if (mclass->hasParent(objectmodel::ContextObject::GetClass())) +// v.push_back("ContextObject"); +// if (mclass->hasParent(visual::VisualModel::GetClass())) +// v.push_back("VisualModel"); +// if (mclass->hasParent(BehaviorModel::GetClass())) +// v.push_back("BehaviorModel"); +// if (mclass->hasParent(CollisionModel::GetClass())) +// v.push_back("CollisionModel"); +// if (mclass->hasParent(behavior::BaseMechanicalState::GetClass())) +// v.push_back("MechanicalState"); +// // A Mass is a technically a ForceField, but we don't want it to appear in the ForceField category +// if (mclass->hasParent(behavior::BaseForceField::GetClass()) && !mclass->hasParent(behavior::BaseMass::GetClass())) +// v.push_back("ForceField"); +// if (mclass->hasParent(behavior::BaseInteractionForceField::GetClass())) +// v.push_back("InteractionForceField"); +// if (mclass->hasParent(behavior::BaseProjectiveConstraintSet::GetClass())) +// v.push_back("ProjectiveConstraintSet"); +// if (mclass->hasParent(behavior::BaseConstraintSet::GetClass())) +// v.push_back("ConstraintSet"); +// if (mclass->hasParent(BaseMapping::GetClass())) +// v.push_back("Mapping"); +// if (mclass->hasParent(DataEngine::GetClass())) +// v.push_back("Engine"); +// if (mclass->hasParent("BaseSimulationExporter")) +// v.push_back("Exporter"); +// if (mclass->hasParent(topology::TopologicalMapping::GetClass())) +// v.push_back("TopologicalMapping"); +// if (mclass->hasParent(behavior::BaseMass::GetClass())) +// v.push_back("Mass"); +// if (mclass->hasParent(behavior::OdeSolver::GetClass())) +// v.push_back("OdeSolver"); +// if (mclass->hasParent(behavior::ConstraintSolver::GetClass())) +// v.push_back("ConstraintSolver"); +// if (mclass->hasParent(behavior::BaseConstraintCorrection::GetClass())) +// v.push_back("ConstraintSolver"); +// if (mclass->hasParent(behavior::BaseMatrixLinearSystem::GetClass())) +// v.push_back("LinearSystem"); +// if (mclass->hasParent(behavior::BaseOrderingMethod::GetClass())) +// v.push_back("OrderingMethod"); +// if (mclass->hasParent(behavior::LinearSolver::GetClass())) +// v.push_back("LinearSolver"); +// if (mclass->hasParent(behavior::BaseAnimationLoop::GetClass())) +// v.push_back("AnimationLoop"); +// // Just like Mass and ForceField, we don't want TopologyObject to appear in the Topology category +// if (mclass->hasParent(topology::Topology::GetClass()) && !mclass->hasParent(topology::BaseTopologyObject::GetClass())) +// v.push_back("Topology"); +// if (mclass->hasParent(topology::BaseTopologyObject::GetClass())) +// v.push_back("TopologyObject"); +// if (mclass->hasParent(behavior::BaseController::GetClass())) +// v.push_back("Controller"); +// if (mclass->hasParent(loader::BaseLoader::GetClass())) +// v.push_back("Loader"); +// if (mclass->hasParent(collision::CollisionAlgorithm::GetClass())) +// v.push_back("CollisionAlgorithm"); +// if (mclass->hasParent(collision::Pipeline::GetClass())) +// v.push_back("CollisionAlgorithm"); +// if (mclass->hasParent(collision::Intersection::GetClass())) +// v.push_back("CollisionAlgorithm"); +// if (mclass->hasParent(objectmodel::ConfigurationSetting::GetClass())) +// v.push_back("ConfigurationSetting"); +// if (v.empty()) +// v.push_back("_Miscellaneous"); +// } +// +// +// } // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h index a7a71ebfccd..cce84c415cf 100644 --- a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h +++ b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h @@ -1,83 +1,83 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -namespace sofa::core -{ - - -using Creator = sofa::core::ObjectFactory::BaseObjectCreator; - -/** - * \brief An Generic Category of the Sofa Library - * - * It contains all the components available for Sofa corresponding to a given category (force field, mass, mapping...) - * This Interface is used for the Modeler mainly. - * - */ -class SOFA_CORE_API CategoryLibrary -{ -public: - typedef std::vector< ComponentLibrary* > VecComponent; - typedef VecComponent::const_iterator VecComponentIterator; - - CategoryLibrary( const std::string &categoryName); - virtual ~CategoryLibrary() {} - - virtual ComponentLibrary *addComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles); - virtual void endConstruction(); - - const std::string &getName() const { return name;} - const VecComponent &getComponents() const {return components;} - - const ComponentLibrary *getComponent( const std::string &componentName) const; - - size_t getNumComponents() const {return components.size();} - - /** \brief Get the list of categories a class belongs to, based on its parent classes. - * - * @param mclass the class the categorize - * @param outputVector the vector where to push the categories - * - * The categories are: AnimationLoop, BehaviorModel, - * CollisionAlgorithm, CollisionAlgorithm, CollisionAlgorithm, - * CollisionModel, ConfigurationSetting, ConstraintSet, - * ConstraintSolver, ConstraintSolver, ContextObject, Controller, - * Engine, Exporter, ForceField, InteractionForceField, LinearSolver, LinearSystem, - * Loader, Mapping, Mass, MechanicalState, OdeSolver, OrderingMethod, - * ProjectiveConstraintSet, TopologicalMapping, Topology, - * TopologyObject, and VisualModel - */ - static void getCategories(const sofa::core::objectmodel::BaseClass* mclass, - std::vector& outputVector); - static std::vector getCategories(); - -protected: - virtual ComponentLibrary *createComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) {return new ComponentLibrary(componentName, name, entry, exampleFiles);} - - std::string name; - VecComponent components; -}; - -} // namespace sofa::core +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// #pragma once +// +// #include +// +// namespace sofa::core +// { +// +// +// using Creator = sofa::core::ObjectFactory::BaseObjectCreator; +// +// /** +// * \brief An Generic Category of the Sofa Library +// * +// * It contains all the components available for Sofa corresponding to a given category (force field, mass, mapping...) +// * This Interface is used for the Modeler mainly. +// * +// */ +// class SOFA_CORE_API CategoryLibrary +// { +// public: +// typedef std::vector< ComponentLibrary* > VecComponent; +// typedef VecComponent::const_iterator VecComponentIterator; +// +// CategoryLibrary( const std::string &categoryName); +// virtual ~CategoryLibrary() {} +// +// virtual ComponentLibrary *addComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles); +// virtual void endConstruction(); +// +// const std::string &getName() const { return name;} +// const VecComponent &getComponents() const {return components;} +// +// const ComponentLibrary *getComponent( const std::string &componentName) const; +// +// size_t getNumComponents() const {return components.size();} +// +// /** \brief Get the list of categories a class belongs to, based on its parent classes. +// * +// * @param mclass the class the categorize +// * @param outputVector the vector where to push the categories +// * +// * The categories are: AnimationLoop, BehaviorModel, +// * CollisionAlgorithm, CollisionAlgorithm, CollisionAlgorithm, +// * CollisionModel, ConfigurationSetting, ConstraintSet, +// * ConstraintSolver, ConstraintSolver, ContextObject, Controller, +// * Engine, Exporter, ForceField, InteractionForceField, LinearSolver, LinearSystem, +// * Loader, Mapping, Mass, MechanicalState, OdeSolver, OrderingMethod, +// * ProjectiveConstraintSet, TopologicalMapping, Topology, +// * TopologyObject, and VisualModel +// */ +// static void getCategories(const sofa::core::objectmodel::BaseClass* mclass, +// std::vector& outputVector); +// static std::vector getCategories(); +// +// protected: +// virtual ComponentLibrary *createComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) {return new ComponentLibrary(componentName, name, entry, exampleFiles);} +// +// std::string name; +// VecComponent components; +// }; +// +// } // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h new file mode 100644 index 00000000000..f842c1196e3 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h @@ -0,0 +1,58 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include + +#include + +namespace sofa::core +{ + +struct SOFA_CORE_API BaseComponentCreator +{ + virtual ~BaseComponentCreator() = default; + virtual objectmodel::BaseComponent::SPtr create() const = 0; + virtual const objectmodel::BaseClass* getClass() = 0; + virtual std::unique_ptr clone() const = 0; +}; + +template +struct ComponentCreator : public BaseComponentCreator +{ + objectmodel::BaseComponent::SPtr create() const override + { + return sofa::core::objectmodel::New(); + } + + const objectmodel::BaseClass* getClass() override + { + return RealComponent::GetClass(); + } + + std::unique_ptr clone() const override + { + return std::make_unique(); + } +}; + +} diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp new file mode 100644 index 00000000000..b3cb77bbfbf --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -0,0 +1,269 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include +#include + +namespace sofa::core +{ + +std::vector ComponentFactory::getComponentsFromName(const std::string& componentName) const +{ + std::vector result; + + std::string componentToSearch = componentName; + + using sofa::helper::lifecycle::renamedComponents; + auto renamedComponent = renamedComponents.find(componentName); + if( renamedComponent != renamedComponents.end() ) + { + componentToSearch = renamedComponent->second.getNewName(); + } + + for (const auto& component : m_registry) + { + const auto fullName = component->componentNamespace + "." + component->componentName; + + if (component->componentName == componentToSearch || fullName == componentToSearch) + { + result.push_back(component); + } + else + { + for (const auto& alias : component->aliases) + { + const auto fullNameAlias = component->componentNamespace + "." + alias; + + if (alias == componentToSearch || fullNameAlias == componentToSearch) + { + result.push_back(component); + } + } + } + } + + return result; +} + +typedef struct ObjectRegistrationEntry +{ + inline static const char* symbol = "registerObjects"; + typedef void (*FuncPtr) (sofa::core::ComponentFactory*); + FuncPtr func; + void operator()(sofa::core::ComponentFactory* data) const + { + if (func) return func(data); + } + ObjectRegistrationEntry() :func(nullptr) {} +} ObjectRegistrationEntry; + +bool ComponentFactory::registerObjectsFromPlugin(const std::string& pluginName) +{ + sofa::helper::system::PluginManager& pluginManager = sofa::helper::system::PluginManager::getInstance(); + auto* plugin = pluginManager.getPlugin(pluginName); + if (plugin == nullptr) + { + msg_error("ObjectFactory") << pluginName << " has not been loaded yet."; + return false; + } + + // do not register if it was already done before + if(m_registeredPluginSet.contains(pluginName)) + { + // This warning should be generalized (i.e not only in dev mode) when runSofa will not auto-load modules/plugins by default anymore + // Commented warning since it is triggered even for SOFA meta-modules (e.g. Sofa.Components) + // dmsg_warning("ObjectFactory") << pluginName << " has already registered its components."; + return false; + } + + ObjectRegistrationEntry registerObjects; + if (pluginManager.getEntryFromPlugin(plugin, registerObjects)) + { + registerObjects(this); + m_registeredPluginSet.insert(pluginName); + return true; + } + else + { + return false; + } +} + +bool ComponentFactory::registerObjects(ComponentRegistrationData& ro) +{ + auto& creators = ro.creators; + + if (creators.empty()) + { + msg_error() << "No creator provided"; + return false; + } + + for (auto& creator : ro.creators) + { + ComponentDescription::SPtr component = std::make_shared(); + + component->componentName = ro.componentName; + component->aliases = ro.aliases; + component->componentNamespace = ro.componentNamespace; + component->componentModule = ro.componentModule; + + component->description = ro.description; + component->authors = sofa::helper::join(ro.authors, ","); + component->license = ro.license; + component->documentationURL = ro.documentationURL; + + { + //special cases for official documentation + const auto modulePaths = sofa::helper::split(component->componentModule, '.'); + if (modulePaths.size() > 2 && modulePaths[0] == "Sofa" && modulePaths[1] == "Component") + { + std::string officialDocURL = std::string(sofa::SOFA_DOCUMENTATION_URL) + std::string("components/"); + officialDocURL += sofa::helper::join(modulePaths.begin() + 2, modulePaths.end(), + [](const std::string& m){ return sofa::helper::downcaseString(m);}, "/"); + officialDocURL += std::string("/") + sofa::helper::downcaseString(component->componentName); + + component->documentationURL.insert(officialDocURL); + } + } + + component->creator = std::move(creator); + + this->m_registry.push_back(component); + } + + return true; + +} + +objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + if (!arg) + return nullptr; + + const char* typeAttribute = arg->getAttribute( "type", nullptr); + + if (typeAttribute == nullptr) + return nullptr; + + std::string classname {typeAttribute}; + + auto candidates = this->getComponentsFromName(classname); + + if (candidates.empty()) + { + //try to load a potential plugin based on the component name + auto lastDot = classname.find_last_of('.'); + if (lastDot != std::string::npos) + { + auto pluginName = classname.substr(0, lastDot); + auto status = helper::system::PluginManager::getInstance().loadPluginByName(pluginName); + if (status == helper::system::PluginManager::PluginLoadStatus::SUCCESS) + { + this->registerObjectsFromPlugin(pluginName); + candidates = this->getComponentsFromName(classname); + } + } + } + + if (candidates.empty()) + { + return nullptr; + } + + std::sort(candidates.begin(), candidates.end(), + [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); + + std::erase_if(candidates, [](const ComponentDescription::SPtr& candidate) + { + return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); + }); + + for (const auto& component : candidates) + { + + } + + const auto firstCandidate = candidates.front(); + + firstCandidate->componentModule; + + auto component = candidates.front()->creator->create(); + + if (component) + { + if (context) + { + context->addObject(component); + } + + component->parse(arg); + } + + return component; +} + +bool ComponentFactory::hasCreator(const std::string& classname) const +{ + return std::any_of(m_registry.begin(), m_registry.end(), + [&](const auto& component){ return component->componentName == classname; }); +} + +void ComponentFactory::getEntriesFromTarget(std::vector& result, + const std::string& target) const +{ + for (const auto& component : m_registry) + { + if (component->componentModule == target) + { + result.push_back(component); + } + } +} + +std::string ComponentFactory::listClassesFromTarget(std::string target, std::string separator) +{ + std::vector entries; + this->getEntriesFromTarget(entries, target); + return sofa::helper::join(entries, separator); +} + +ComponentFactory* ComponentFactory::getInstance() { return MainComponentFactory::getInstance(); } +objectmodel::BaseComponent::SPtr ComponentFactory::CreateObject( + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + return MainComponentFactory::CreateComponent(context, arg); +} + +bool ComponentFactory::HasCreator(const std::string& classname) +{ + return MainComponentFactory::HasCreator(classname); +} + +bool MainComponentFactory::HasCreator(const std::string& classname) +{ + return getInstance()->hasCreator(classname); +} + +} // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h new file mode 100644 index 00000000000..9aa05debcc9 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -0,0 +1,187 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include + +namespace sofa::core +{ + +struct SOFA_CORE_API ComponentDescription +{ + using SPtr = std::shared_ptr; + + std::string componentName; + std::set aliases; + + std::set templateAttributes; + std::string componentNamespace; + std::string componentModule; + + unsigned int instantiationPriority {}; + + std::string description; + std::string authors; + std::string license; + std::set documentationURL; + + std::unique_ptr creator; +}; + +class SOFA_CORE_API ComponentFactory +{ +public: + + // to deprecate + using ClassEntry = ComponentDescription; + + std::vector getComponentsFromName(const std::string& componentName) const; + + bool registerObjectsFromPlugin(const std::string& pluginName); + bool registerObjects(ComponentRegistrationData& ro); + + + + /// Create a component given a context and a description. + objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + + + + /// Test if a creator exists for a given classname + bool hasCreator(const std::string& classname) const; + + /// Fill the given vector with the registered classes from a given target + void getEntriesFromTarget(std::vector& result, const std::string& target) const; + + /// Return the list of classes from a given target + std::string listClassesFromTarget(std::string target, std::string separator = ", "); + + /// Fill the given vector with all the registered classes derived from BaseClass + template + std::vector getEntriesDerivedFrom() const; + + /// Return the list of classes derived from BaseClass as a string + template + std::string listClassesDerivedFrom(const std::string& separator = ", ") const; + + + static SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::getInstance instead") ComponentFactory* getInstance(); + static SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::CreateComponent instead") objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + static bool SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::HasCreator instead") HasCreator(const std::string& classname); + + + void getEntry(std::string) = delete; + +protected: + + /// Keep track of plugins who already registered + using RegisteredPluginSet = std::set; + RegisteredPluginSet m_registeredPluginSet; + + std::vector m_registry; +}; + + +template +std::vector ComponentFactory::getEntriesDerivedFrom() const +{ + std::vector result; + + auto* componentClass = Class::GetClass(); + if (!componentClass) + { + return result; + } + + for (const auto& component : m_registry) + { + if (auto* componentClassInRegistry = component->creator->getClass()) + { + if (componentClassInRegistry->hasParent(componentClass)) + { + result.push_back(component); + } + } + } + + return result; +} + +template +std::string ComponentFactory::listClassesDerivedFrom(const std::string& separator) const +{ + auto entries = getEntriesDerivedFrom(); + + return sofa::helper::join(entries.begin(), entries.end(), + [](const ComponentDescription::SPtr& entry){ return entry->componentName;}, separator); +} + + + + +struct SOFA_CORE_API MainComponentFactory +{ + static ComponentFactory* getInstance() + { + static ComponentFactory instance; + return &instance; + } + + static objectmodel::BaseComponent::SPtr CreateComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) + { + return getInstance()->createComponent(context, arg); + } + + //to deprecate + static objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) + { + return CreateComponent(context, arg); + } + + /// Test if a creator exists for a given classname + static bool HasCreator(const std::string& classname); +}; + + +} + + + + + + + +namespace sofa::helper::logging +{ + +inline bool notMuted(const core::ComponentFactory*) +{ + return true; +} + +inline ComponentInfo::SPtr getComponentInfo(const core::ComponentFactory*) +{ + return std::make_shared("ComponentFactory"); +} +} diff --git a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp index 491e001d3c9..265889f3919 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp @@ -1,110 +1,110 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include - -namespace sofa::core -{ - -std::string caseInsensitive(const std::string &text) -{ - std::string result; result.resize(text.size()); - for (unsigned int i=0; i(toupper(text[i])); - return result; -} - -//------------------------------------------------------------------------------------------------------- -ComponentLibrary::ComponentLibrary( const std::string &componentN, const std::string &categoryN, ClassEntry::SPtr e, const std::vector< std::string > &exampleFiles): name(componentN), categoryName(categoryN),entry(e) -{ - - description = std::string("

") + entry->className + std::string(": "); - - std::vector< std::string > possiblePaths; - - std::vector categories; - const objectmodel::BaseClass* entryClass = entry->creatorMap.begin()->second->getClass(); - CategoryLibrary::getCategories(entryClass, categories); - for (std::vector< std::string >::iterator it=categories.begin(); it!=categories.end() ; ++it) - { - if (it != categories.begin()) description += std::string(", "); - description += (*it); - } - - //Find a scene - std::string nameComponentCaseInsensitive = caseInsensitive(entry->className); - - for (unsigned int i=0; iclassName.c_str()) >= 0 ) - if (exampleCaseInsensitive.find(nameComponentCaseInsensitive) != std::string::npos) - possiblePaths.push_back(exampleFiles[i]); - } - - std::string nameSpace = sofa::helper::NameDecoder::decodeNamespaceName(entry->creatorMap.begin()->second->type()); - - description += std::string("

"); - - description += std::string("
    "); - - description += std::string("
  • Description: ") + entry->description + std::string("
  • "); - - - if (!nameSpace.empty()) - description += std::string("
  • NameSpace: ")+nameSpace +std::string("
  • "); - if (!entry->authors.empty()) - description += std::string("
  • Authors: ")+entry->authors +std::string("
  • "); - if (!entry->license.empty()) - description += std::string("
  • License: ") + entry->license + std::string("
  • "); - if (!entry->documentationURL.empty()) - description += std::string("
  • Documentation: ") + entry->documentationURL + std::string("
  • "); - - if (possiblePaths.size() != 0) - { - description += std::string("
  • Example: "); - } - - description += std::string("
"); -} - - - - - -void ComponentLibrary::addTemplate( const std::string &nameT) -{ - if (nameT.empty()) return; - templateName.push_back(nameT); -} - - -void ComponentLibrary::endConstruction() -{ -} - -} +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// +// #include +// #include +// +// namespace sofa::core +// { +// +// std::string caseInsensitive(const std::string &text) +// { +// std::string result; result.resize(text.size()); +// for (unsigned int i=0; i(toupper(text[i])); +// return result; +// } +// +// //------------------------------------------------------------------------------------------------------- +// ComponentLibrary::ComponentLibrary( const std::string &componentN, const std::string &categoryN, ClassEntry::SPtr e, const std::vector< std::string > &exampleFiles): name(componentN), categoryName(categoryN),entry(e) +// { +// +// description = std::string("

") + entry->className + std::string(": "); +// +// std::vector< std::string > possiblePaths; +// +// std::vector categories; +// const objectmodel::BaseClass* entryClass = entry->creatorMap.begin()->second->getClass(); +// CategoryLibrary::getCategories(entryClass, categories); +// for (std::vector< std::string >::iterator it=categories.begin(); it!=categories.end() ; ++it) +// { +// if (it != categories.begin()) description += std::string(", "); +// description += (*it); +// } +// +// //Find a scene +// std::string nameComponentCaseInsensitive = caseInsensitive(entry->className); +// +// for (unsigned int i=0; iclassName.c_str()) >= 0 ) +// if (exampleCaseInsensitive.find(nameComponentCaseInsensitive) != std::string::npos) +// possiblePaths.push_back(exampleFiles[i]); +// } +// +// std::string nameSpace = sofa::helper::NameDecoder::decodeNamespaceName(entry->creatorMap.begin()->second->type()); +// +// description += std::string("

"); +// +// description += std::string("
    "); +// +// description += std::string("
  • Description: ") + entry->description + std::string("
  • "); +// +// +// if (!nameSpace.empty()) +// description += std::string("
  • NameSpace: ")+nameSpace +std::string("
  • "); +// if (!entry->authors.empty()) +// description += std::string("
  • Authors: ")+entry->authors +std::string("
  • "); +// if (!entry->license.empty()) +// description += std::string("
  • License: ") + entry->license + std::string("
  • "); +// if (!entry->documentationURL.empty()) +// description += std::string("
  • Documentation: ") + entry->documentationURL + std::string("
  • "); +// +// if (possiblePaths.size() != 0) +// { +// description += std::string("
  • Example: "); +// } +// +// description += std::string("
"); +// } +// +// +// +// +// +// void ComponentLibrary::addTemplate( const std::string &nameT) +// { +// if (nameT.empty()) return; +// templateName.push_back(nameT); +// } +// +// +// void ComponentLibrary::endConstruction() +// { +// } +// +// } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h index c886bcc2b01..b37751d5426 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h @@ -1,63 +1,63 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -namespace sofa::core -{ - -typedef sofa::core::ObjectFactory::ClassEntry ClassEntry; - -/** - * \brief An Generic Component of the Sofa Library - * - * It contains all the information related to a Sofa component: its name, the templates available, a description of it, its creator, ... - * This Interface is used for the Modeler mainly. - * - */ -class SOFA_CORE_API ComponentLibrary -{ -public: - ComponentLibrary(const std::string& componentName, const std::string& categoryName, ClassEntry::SPtr entry, const std::vector< std::string >& exampleFiles); - virtual ~ComponentLibrary() {} - - virtual void addTemplate( const std::string& templateName); - virtual void endConstruction(); - virtual void setDisplayed(bool ) {} - - const std::string& getName() const { return name;} - const std::string& getDescription() const { return description;} - const std::string& getCategory() const { return categoryName;} - const std::vector< std::string >& getTemplates() const { return templateName;} - const ClassEntry::SPtr getEntry() const { return entry;} - -protected: - //-------------------------------------------- - //Sofa information - std::string name; - std::vector< std::string > templateName; - std::string description; - std::string categoryName; - ClassEntry::SPtr entry; -}; -} +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// #pragma once +// +// #include +// +// namespace sofa::core +// { +// +// typedef sofa::core::ObjectFactory::ClassEntry ClassEntry; +// +// /** +// * \brief An Generic Component of the Sofa Library +// * +// * It contains all the information related to a Sofa component: its name, the templates available, a description of it, its creator, ... +// * This Interface is used for the Modeler mainly. +// * +// */ +// class SOFA_CORE_API ComponentLibrary +// { +// public: +// ComponentLibrary(const std::string& componentName, const std::string& categoryName, ClassEntry::SPtr entry, const std::vector< std::string >& exampleFiles); +// virtual ~ComponentLibrary() {} +// +// virtual void addTemplate( const std::string& templateName); +// virtual void endConstruction(); +// virtual void setDisplayed(bool ) {} +// +// const std::string& getName() const { return name;} +// const std::string& getDescription() const { return description;} +// const std::string& getCategory() const { return categoryName;} +// const std::vector< std::string >& getTemplates() const { return templateName;} +// const ClassEntry::SPtr getEntry() const { return entry;} +// +// protected: +// //-------------------------------------------- +// //Sofa information +// std::string name; +// std::vector< std::string > templateName; +// std::string description; +// std::string categoryName; +// ClassEntry::SPtr entry; +// }; +// } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h new file mode 100644 index 00000000000..7bb36a8c5fd --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -0,0 +1,167 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sofa::core +{ + +class SOFA_CORE_API ComponentRegistrationData +{ +public: + + std::string componentName; + std::set aliases; + std::string description; + + std::set authors; + std::string license; + std::set documentationURL; + std::string componentNamespace; + std::string componentModule; + + std::optional defaultTemplateId; + + std::vector> creators; + + /// Start the registration by giving the description of this class. + explicit ComponentRegistrationData(const std::string& description) + { + if (!description.empty()) + { + addDescription(description); + } + } + + ComponentRegistrationData(const ComponentRegistrationData&) = delete; + void operator=(const ComponentRegistrationData&) = delete; + + /// Add an alias name for this class + ComponentRegistrationData& addAlias(std::string val) + { + aliases.insert(val); + return *this; + } + + /// Add more descriptive text about this class + ComponentRegistrationData& addDescription(const std::string& val) + { + if (description.empty()) + { + description = val; + } + else + { + dmsg_error("ComponentRegistrationData") << "Trying to add multiple descriptions for a single component whereas only one is supported"; + } + return *this; + } + + /// Specify a list of authors (separated with spaces) + ComponentRegistrationData& addAuthor(std::string val) + { + authors.insert(val); + return *this; + } + + /// Specify a license (LGPL, GPL, ...) + ComponentRegistrationData& addLicense(std::string val) + { + if (license.empty()) + { + license = val; + } + else + { + dmsg_error("ComponentRegistrationData") << "Trying to add multiple licenses for a single component whereas only one is supported"; + } + return *this; + } + + /// Specify a documentation URL + ComponentRegistrationData& addDocumentationURL(std::string url) + { + documentationURL.insert(url); + return *this; + } + + /// Add a template instantiation of this class. + /// + /// \param defaultTemplate set to true if this should be the default instance when no template name is given. + template + ComponentRegistrationData& add(bool defaultTemplate = false) + { +#ifdef SOFA_TARGET + const std::string target = sofa_tostring(SOFA_TARGET); + + if (!target.empty()) + { + componentNamespace = target; + componentModule = target; + } +#else + dmsg_warning("ComponentFactory") << "Module name cannot be found when registering " + << RealObject::GetClass()->className << "<" << RealObject::GetClass()->templateName << "> into the component factory"; +#endif + + const std::string classname = sofa::core::objectmodel::BaseClassNameHelper::getClassName(); + if (componentName.empty()) + { + componentName = classname; + } + else + { + if (componentName != classname) + { + msg_error("ComponentFactory") << "Trying to define a class (" << classname << ") unrelated to " << componentName; + return *this; + } + } + + creators.push_back(std::unique_ptr(new ComponentCreator)); + + if (defaultTemplate) + { + if (defaultTemplateId.has_value()) + { + msg_error("ComponentFactory") << "Trying to define a default template for " + << RealObject::GetClass()->className << " whereas one was already defined before"; + } + else + { + defaultTemplateId = creators.size() - 1; + } + } + + return *this; + } + +}; + +} diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp b/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp deleted file mode 100644 index 24947674ec5..00000000000 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp +++ /dev/null @@ -1,855 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include - -#include -#include -#include -#include -#include -#include - -namespace sofa::core -{ - -ObjectFactory::~ObjectFactory() -{ -} - -ObjectFactory::ClassEntry& ObjectFactory::getEntry(std::string classname) -{ - if (!registry.contains(classname)) - { - registry[classname] = std::make_shared(); - registry[classname]->className = classname; - } - - return *registry[classname]; -} - -/// Test if a creator exists for a given classname -bool ObjectFactory::hasCreator(std::string classname) -{ - const ClassEntryMap::iterator it = registry.find(classname); - if (it == registry.end()) - return false; - const ClassEntry::SPtr entry = it->second; - return (!entry->creatorMap.empty()); -} - -std::string ObjectFactory::shortName(std::string classname) -{ - const ClassEntryMap::iterator it = registry.find(classname); - if (it != registry.end()) - { - const ClassEntry::SPtr entry = it->second; - if(!entry->creatorMap.empty()) - { - const auto firstElement = entry->creatorMap.begin(); - const BaseObjectCreator::SPtr c = firstElement->second; - return c->getClass()->shortName; - } - } - return {}; -} - -bool ObjectFactory::addAlias(std::string name, std::string target, bool force, - ClassEntry::SPtr* previous) -{ - // Check that the pointed class does exist - const ClassEntryMap::iterator it = registry.find(target); - if (it == registry.end()) - { - msg_error("ObjectFactory::addAlias()") << "Target class for alias '" << target << "' not found: " << name; - return false; - } - - const ClassEntry::SPtr& pointedEntry = it->second; - ClassEntry::SPtr& aliasEntry = registry[name]; - - // Check that the alias does not already exist, unless 'force' is true - if (aliasEntry.get()!=nullptr && !force) - { - msg_error("ObjectFactory::addAlias()") << "Name already exists: " << name; - return false; - } - - if (previous) - { - const ClassEntry::SPtr& entry = aliasEntry; - *previous = entry; - } - - registry[name] = pointedEntry; - pointedEntry->aliases.insert(name); - return true; -} - -void ObjectFactory::resetAlias(std::string name, ClassEntry::SPtr previous) -{ - registry[name] = previous; -} - - -void findTemplatedCreator( - objectmodel::BaseContext* context, - const ObjectFactory::BaseObjectCreator::SPtr& creator, const std::string& templateName, - std::map>& creatorsErrors, - std::vector< std::pair >& creators, - objectmodel::BaseObjectDescription* arg) -{ - if (helper::system::PluginManager::getInstance().isPluginUnloaded(creator->getTarget())) - { - creatorsErrors[templateName].emplace_back( - "The object was previously registered, but the module that " - "registered the object has been unloaded, preventing the object creation."); - arg->clearErrors(); - } - else - { - if (creator->canCreate(context, arg)) - { - creators.emplace_back(templateName, creator); - } - else - { - creatorsErrors[templateName] = arg->getErrors(); - arg->clearErrors(); - } - } -} - -objectmodel::BaseComponent::SPtr ObjectFactory::createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) -{ - objectmodel::BaseComponent::SPtr object = nullptr; - std::vector< std::pair > creators; - std::string classname = arg->getAttribute( "type", ""); - std::string usertemplatename = arg->getAttribute( "template", ""); - ClassEntry::SPtr entry ; - - //////////////////////////////////////////////////////////////////////////////////////////////// - /// Process the template aliases. - /// (1) split in a vector the user provided templates by ',' - /// (2) for each entry search if there is an alias - /// (3) if there is none then keep value as is - /// otherwise replace the value with the alias. - /// if there is one and it is "undefined" generate a warning. - /// and "undefined" behavior means that the template is converting a specifically given - /// type precision into a different one. - /// (4) rebuild the template string by joining them all with ','. - std::vector usertemplatenames = sofa::helper::split(usertemplatename, ','); - std::vector deprecatedTemplates; - for(auto& name : usertemplatenames) - { - const sofa::defaulttype::TemplateAlias* alias; - if( (alias=sofa::defaulttype::TemplateAliases::getTemplateAlias(name)) != nullptr ) - { - assert(alias != nullptr); - /// This alias results in "undefined" behavior. - if( alias->second ) - { - deprecatedTemplates.push_back("The deprecated template '"+name+"' has been replaced by "+alias->first+"."); - } - - name = alias->first; - } - } - std::string templatename = sofa::helper::join(usertemplatenames, ","); - std::string userresolved = templatename; // Copy in case we change for the default one - //////////////////////////////////////////////////////////////////////////////////////////////// - - - //Check if object has been renamed - - using sofa::helper::lifecycle::renamedComponents; - auto renamedComponent = renamedComponents.find(classname); - if( renamedComponent != renamedComponents.end() ) - { - classname = renamedComponent->second.getNewName(); - } - - - // In order to get the errors from the creators only, we save the current errors at this point - // and we clear them. Once we extracted the errors from the creators, we put push them back. - std::map> creators_errors; // (template_name, errors) - const auto previous_errors = arg->getErrors(); - arg->clearErrors(); - - // For every classes in the registry - ClassEntryMap::iterator it = registry.find(classname); - if (it != registry.end()) // Found the classname - { - entry = it->second; - // If no template has been given or if the template does not exist, first try with the default one - if(templatename.empty() || !entry->creatorMap.contains(templatename)) - templatename = entry->defaultTemplate; - - - if (auto it2 = entry->creatorMap.find(templatename); - it2 != entry->creatorMap.end()) - { - findTemplatedCreator(context, it2->second, it2->first, creators_errors, creators, arg); - } - - // If object cannot be created with the given template (or the default one), try all possible ones - if (creators.empty()) - { - for (const auto& [creatorTemplateName, creator] : entry->creatorMap) - { - if (creatorTemplateName != templatename) - { - findTemplatedCreator(context, creator, creatorTemplateName, creators_errors, creators, arg); - } - } - } - } - - // Restore previous errors without the errors from the creator - arg->logErrors(previous_errors); - - if (creators.empty()) - { - //// The object cannot be created - arg->logError("Object type " + classname + std::string("<") + templatename + std::string("> was not created")); - - using sofa::helper::lifecycle::ComponentChange; - using sofa::helper::lifecycle::uncreatableComponents; - using sofa::helper::lifecycle::movedComponents; - using sofa::helper::lifecycle::dealiasedComponents; - if(it == registry.end()) - { - arg->logError("The component '" + classname + "' cannot be found in the factory."); - auto uncreatableComponent = uncreatableComponents.find(classname); - auto movedComponent = movedComponents.find(classname); - auto dealiasedComponent = dealiasedComponents.find(classname); - - const bool isUncreatable = uncreatableComponent != uncreatableComponents.end(); - const bool isMoved = movedComponent != movedComponents.end(); - const bool isDealiased = dealiasedComponent != dealiasedComponents.end(); - - const bool multipleReasons = static_cast(isUncreatable) + static_cast(isMoved) + static_cast(isDealiased) > 1; - std::size_t reasonNumber = 1; - const auto number = [&reasonNumber, multipleReasons]() -> std::string - { - return multipleReasons ? std::to_string(reasonNumber++) + ") " : ""; - }; - - if (multipleReasons) - { - arg->logError("Several reasons are possible:"); - } - - if(isUncreatable) - { - arg->logError(number() + uncreatableComponent->second.getMessage() ); - } - if (isMoved) - { - arg->logError(number() + movedComponent->second.getMessage() ); - } - if (isDealiased) - { - arg->logError(number() + dealiasedComponent->second.getMessage()); - } - else - { - std::vector possibleNames; - possibleNames.reserve(registry.size()); - for(auto& k : registry) - { - possibleNames.emplace_back(k.first); - } - - const auto closestMatches = sofa::helper::getClosestMatch(classname, possibleNames, 5, 0.6); - if (!closestMatches.empty()) - { - arg->logError("But the following object(s) exist:"); - for(auto& [name, score] : closestMatches) - { - arg->logError( " : " + name + " ("+ std::to_string((int)(100*score))+"% match)"); - } - } - } - } - else - { - std::stringstream tmp; - tmp << "The object is in the factory but cannot be created." << msgendl; - tmp << "Requested template : " << (usertemplatename.empty() ? "None" : usertemplatename) << msgendl; - if (templatename.empty()) { - tmp << "Used template : None" << msgendl; - } else { - tmp << "Used template : " << templatename; - if (templatename == entry->defaultTemplate) { - tmp << " (default)"; - } - tmp << msgendl; - } - - // Collect the errors from the creator with the specified (or default) template name - auto main_creator_errors_iterator = creators_errors.find(templatename); - if (main_creator_errors_iterator != creators_errors.end()) { - tmp << "Reason(s) : "; - if (main_creator_errors_iterator->second.empty()) { - tmp << "No reasons given" << msgendl; - } else if (main_creator_errors_iterator->second.size() == 1) { - tmp << main_creator_errors_iterator->second[0] << msgendl; - } else { - tmp << msgendl; - for (std::size_t i = 0; i < main_creator_errors_iterator->second.size(); ++i) { - tmp << " " << (i+1) << ". " << main_creator_errors_iterator->second[i] << msgendl; - } - } - creators_errors.erase(main_creator_errors_iterator); - } - - // Collect the errors from the creator with all remaining template names - if (! creators_errors.empty()) { - for (const auto & creator_errors_it : creators_errors) { - const std::string & creator_template_name = creator_errors_it.first; - const std::vector & creator_errors = creator_errors_it.second; - tmp << "Also tried to create the object with the template '"<logError(tmp.str()); - } - return nullptr; - } - - object = creators[0].second->createInstance(context, arg); - assert(object!=nullptr); - - /// The object has been created, but not with the template given by the user - if (!usertemplatename.empty() && object->getTemplateName() != userresolved) - { - std::vector templateList; - if (entry) - for (const auto& cr : entry->creatorMap) - templateList.push_back(cr.first); - std::stringstream ss; - bool isUserTemplateNameInTemplateList = false; - for(unsigned int i = 0; i < templateList.size(); ++i) - { - ss << templateList[i]; - isUserTemplateNameInTemplateList |= (templateList[i] == usertemplatename || templateList[i] == userresolved); - if (i != templateList.size() - 1) - ss << ", "; - } - if (isUserTemplateNameInTemplateList) - { - msg_error(object.get()) << "Requested template '" << usertemplatename << "' " - << "is not compatible with the current context. " - << "Falling back to the first compatible template: '" - << object->getTemplateName() << "'."; - } - else - { - msg_error(object.get()) << "Requested template '" << usertemplatename << "' " - << "cannot be found in the list of available templates [" << ss.str() << "]. " - << "Falling back to the first compatible template: '" - << object->getTemplateName() << "'."; - } - } - else if (creators.size() > 1) - { // There were multiple possibilities, we used the first one (not necessarily the default, as it can be incompatible) - std::string w = "Template '" + templatename + std::string("' incorrect, used ") + object->getTemplateName() + std::string(" in the list:"); - for(unsigned int i = 0; i < creators.size(); ++i) - w += std::string("\n\t* ") + creators[i].first; - msg_warning(object.get()) << w; - } - - ////////////////////////// This code is emitting a warning messages if the scene is loaded - if( m_callbackOnCreate ) - m_callbackOnCreate(object.get(), arg); - - ///////////////////////// All this code is just there to implement the MakeDataAlias component. - std::vector todelete; - for(auto& kv : entry->m_dataAlias) - { - if(object->findData(kv.first)==nullptr) - { - msg_warning(object.get()) << "The object '"<< (object->getClassName()) <<"' does not have an alias named '"<< kv.first <<"'. " - << "To remove this error message you need to use a valid data name for the 'dataname field'. "; - - todelete.push_back(kv.first); - } - } - - for(auto& todeletename : todelete) - { - entry->m_dataAlias.erase( entry->m_dataAlias.find(todeletename) ) ; - } - - for(auto& kv : entry->m_dataAlias) - { - objectmodel::BaseObjectDescription newdesc; - for(std::string& alias : kv.second){ - object->addAlias(object->findData(kv.first), alias.c_str()) ; - - /// The Alias is used in the argument - const std::string val(arg->getAttribute(alias)); - if( !val.empty() ){ - newdesc.setAttribute( alias, val ); - } - } - object->parse(&newdesc); - } - - /// We managed to create an object but there is error message in the log. Thus we emit them - /// as warning to this object. - if(!deprecatedTemplates.empty()) - { - msg_deprecated(object.get()) << sofa::helper::join(deprecatedTemplates, msgendl) ; - } - - return object; -} - -ObjectFactory* ObjectFactory::getInstance() -{ - static ObjectFactory instance; - return &instance; -} - -void ObjectFactory::getAllEntries(std::vector& result, const bool filterUnloadedPlugins) -{ - result.clear(); - for (const auto& [className, entry] : registry) - { - // Push the entry only if it is not an alias - if (entry->className == className) - { - result.push_back(entry); - } - } - - if (filterUnloadedPlugins) - { - for (auto itEntry = result.begin(); itEntry != result.end();) - { - auto& creatorMap = (*itEntry)->creatorMap; - for (auto itCreator = creatorMap.begin(); itCreator != creatorMap.end();) - { - if (helper::system::PluginManager::getInstance().isPluginUnloaded(itCreator->second->getTarget())) - { - itCreator = creatorMap.erase(itCreator); - } - else - { - ++itCreator; - } - } - - if (creatorMap.empty()) - { - itEntry = result.erase(itEntry); - } - else - { - ++itEntry; - } - } - } -} - -void ObjectFactory::getEntriesFromTarget(std::vector& result, std::string target) -{ - result.clear(); - for(ClassEntryMap::iterator it = registry.begin(), itEnd = registry.end(); - it != itEnd; ++it) - { - ClassEntry::SPtr entry = it->second; - if(entry->className == it->first) - { - - bool inTarget = false; - for (ObjectTemplateCreatorMap::iterator itc = entry->creatorMap.begin(), itcend = entry->creatorMap.end(); itc != itcend; ++itc) - { - const BaseObjectCreator::SPtr c = itc->second; - if (target == c->getTarget()) - { - inTarget = true; - break; - } - } - if (inTarget) - result.push_back(entry); - } - } -} - -std::string ObjectFactory::listClassesFromTarget(std::string target, std::string separator) -{ - std::vector entries; - getEntriesFromTarget(entries, target); - std::ostringstream oss; - for (unsigned int i=0; iclassName; - } - std::string result = oss.str(); - return result; -} - -void ObjectFactory::dump(std::ostream& out) -{ - for (ClassEntryMap::iterator it = registry.begin(), itend = registry.end(); it != itend; ++it) - { - const ClassEntry::SPtr entry = it->second; - if (entry->className != it->first) continue; - out << "class " << entry->className <<" :\n"; - if (!entry->aliases.empty()) - { - out << " aliases :"; - for (std::set::iterator myit = entry->aliases.begin(), aliasesEnd = entry->aliases.end(); myit != aliasesEnd; ++myit) - out << " " << *myit; - out << "\n"; - } - if (!entry->description.empty()) - out << entry->description; - if (!entry->authors.empty()) - out << " authors : " << entry->authors << "\n"; - if (!entry->license.empty()) - out << " license : " << entry->license << "\n"; - if (!entry->documentationURL.empty()) - out << " documentation : " << entry->documentationURL << "\n"; - for (ObjectTemplateCreatorMap::iterator itc = entry->creatorMap.begin(), itcend = entry->creatorMap.end(); itc != itcend; ++itc) - { - out << " template instance : " << itc->first << "\n"; - } - } -} - -static std::string xmlencode(const std::string& str) -{ - std::string res; - for (unsigned int i=0; i': res += ">"; break; - case '&': res += "&"; break; - case '"': res += """; break; - case '\'': res += "'"; break; - default: res += str[i]; - } - } - return res; -} - -void ObjectFactory::dumpXML(std::ostream& out) -{ - for (ClassEntryMap::iterator it = registry.begin(), itend = registry.end(); it != itend; ++it) - { - const ClassEntry::SPtr entry = it->second; - if (entry->className != it->first) continue; - out << "className) <<"\">\n"; - for (std::set::iterator myit = entry->aliases.begin(), aliasesEnd = entry->aliases.end(); myit != aliasesEnd; ++myit) - out << "" << xmlencode(*myit) << "\n"; - if (!entry->description.empty()) - out << ""<description<<"\n"; - if (!entry->authors.empty()) - out << ""<authors<<"\n"; - if (!entry->license.empty()) - out << ""<license<<"\n"; - if (!entry->documentationURL.empty()) - out << ""<documentationURL<<"\n"; - for (ObjectTemplateCreatorMap::iterator itc = entry->creatorMap.begin(), itcend = entry->creatorMap.end(); itc != itcend; ++itc) - { - out << "first.empty()) out << " template=\"" << xmlencode(itc->first) << "\""; - out << "/>\n"; - } - out << "\n"; - } -} - -void ObjectFactory::dumpHTML(std::ostream& out) -{ - out << "
    \n"; - for (ClassEntryMap::iterator it = registry.begin(), itend = registry.end(); it != itend; ++it) - { - const ClassEntry::SPtr entry = it->second; - if (entry->className != it->first) continue; - out << "
  • " << xmlencode(entry->className) <<"\n"; - if (!entry->description.empty()) - out << "
    "<description<<"\n"; - out << "
      \n"; - if (!entry->aliases.empty()) - { - out << "
    • Aliases:"; - for (std::set::iterator myit = entry->aliases.begin(), aliasesEnd = entry->aliases.end(); myit != aliasesEnd; ++myit) - out << " " << xmlencode(*myit); - out << "
    • \n"; - } - if (!entry->authors.empty()) - out << "
    • Authors: "<authors<<"
    • \n"; - if (!entry->license.empty()) - out << "
    • License: "<license<<"
    • \n"; - if (!entry->documentationURL.empty()) - out << "
    • Documentation: "<documentationURL<<"
    • \n"; - if (entry->creatorMap.size()>2 || (entry->creatorMap.size()==1 && !entry->creatorMap.begin()->first.empty())) - { - out << "
    • Template instances:"; - for (ObjectTemplateCreatorMap::iterator itc = entry->creatorMap.begin(), itcend = entry->creatorMap.end(); itc != itcend; ++itc) - { - if (itc->first == entry->defaultTemplate) - out << " " << xmlencode(itc->first) << ""; - else - out << " " << xmlencode(itc->first); - } - out << "
    • \n"; - } - out << "
    \n"; - out << "
  • \n"; - } - out << "
\n"; -} - -bool ObjectFactory::registerObjects(ObjectRegistrationData& ro) -{ - return ro.commitTo(this); -} - -ObjectRegistrationData::ObjectRegistrationData(const std::string& description) -{ - if (!description.empty()) - { - addDescription(description); - } -} - -ObjectRegistrationData& ObjectRegistrationData::addAlias(std::string val) -{ - entry.aliases.insert(val); - return *this; -} - -ObjectRegistrationData& ObjectRegistrationData::addDescription(std::string val) -{ - val += '\n'; - entry.description += val; - return *this; -} - -ObjectRegistrationData& ObjectRegistrationData::addAuthor(std::string val) -{ - val += ' '; - entry.authors += val; - return *this; -} - -ObjectRegistrationData& ObjectRegistrationData::addLicense(std::string val) -{ - entry.license += val; - return *this; -} - -ObjectRegistrationData& ObjectRegistrationData::addDocumentationURL(std::string url) -{ - entry.documentationURL += url; - return *this; -} - -ObjectRegistrationData& ObjectRegistrationData::addCreator(std::string classname, - std::string templatename, - ObjectFactory::BaseObjectCreator::SPtr creator) -{ - - if (!entry.className.empty() && entry.className != classname) - { - msg_error("ObjectFactory") << "Template already instantiated with a different classname: " << entry.className << " != " << classname; - } - else if (entry.creatorMap.contains(templatename)) - { - msg_error("ObjectFactory") << "Component already registered: " << classname << "<" << templatename << ">"; - } - else - { - entry.className = classname; - entry.creatorMap[templatename] = creator; - } - return *this; -} - -bool ObjectRegistrationData::commitTo(sofa::core::ObjectFactory* objectFactory) const -{ - if (entry.className.empty() || objectFactory == nullptr) - { - return false; - } - else - { - ObjectFactory::ClassEntry& reg = objectFactory->getEntry(entry.className); - reg.description += entry.description; - reg.authors += entry.authors; - reg.license += entry.license; - reg.documentationURL += entry.documentationURL; - if (!entry.defaultTemplate.empty()) - { - if (!reg.defaultTemplate.empty()) - { - msg_warning("ObjectFactory") << "Default template for class " << entry.className << " already registered (" << reg.defaultTemplate << "), do not register " << entry.defaultTemplate << " as the default"; - } - else - { - reg.defaultTemplate = entry.defaultTemplate; - } - } - for (const auto& creator_entry : entry.creatorMap) - { - const std::string & template_name = creator_entry.first; - const auto [it, success] = reg.creatorMap.insert(creator_entry); - if (!success) - { - std::string classType = entry.className; - if (!template_name.empty()) - { - classType += "<" + template_name + ">"; - } - - msg_warning("ObjectFactory") << "Class already registered in the ObjectFactory: " << classType; - } - } - - for (const auto & alias : entry.aliases) - { - if (!reg.aliases.contains(alias)) - { - objectFactory->addAlias(alias,entry.className); - } - } - return true; - } - -} - -typedef struct ObjectRegistrationEntry -{ - inline static const char* symbol = "registerObjects"; - typedef void (*FuncPtr) (sofa::core::ObjectFactory*); - FuncPtr func; - void operator()(sofa::core::ObjectFactory* data) - { - if (func) return func(data); - } - ObjectRegistrationEntry() :func(nullptr) {} -} ObjectRegistrationEntry; - -bool ObjectFactory::registerObjectsFromPlugin(const std::string& pluginName) -{ - sofa::helper::system::PluginManager& pluginManager = sofa::helper::system::PluginManager::getInstance(); - auto* plugin = pluginManager.getPlugin(pluginName); - if (plugin == nullptr) - { - msg_error("ObjectFactory") << pluginName << " has not been loaded yet."; - return false; - } - - // do not register if it was already done before - if(m_registeredPluginSet.contains(pluginName)) - { - // This warning should be generalized (i.e not only in dev mode) when runSofa will not auto-load modules/plugins by default anymore - // Commented warning since it is triggered even for SOFA meta-modules (e.g. Sofa.Components) - // dmsg_warning("ObjectFactory") << pluginName << " has already registered its components."; - return false; - } - - ObjectRegistrationEntry registerObjects; - if (pluginManager.getEntryFromPlugin(plugin, registerObjects)) - { - registerObjects(this); - m_registeredPluginSet.insert(pluginName); - return true; - } - else - { - return false; - } -} - -RegisterObject::RegisterObject(const std::string& description) - : m_objectRegistrationdata(description) -{ - -} - -RegisterObject& RegisterObject::addAlias(std::string val) -{ - m_objectRegistrationdata.addAlias(val); - return *this; -} - -RegisterObject& RegisterObject::addDescription(std::string val) -{ - m_objectRegistrationdata.addDescription(val); - return *this; -} - -RegisterObject& RegisterObject::addAuthor(std::string val) -{ - m_objectRegistrationdata.addAuthor(val); - return *this; -} - -RegisterObject& RegisterObject::addLicense(std::string val) -{ - m_objectRegistrationdata.addLicense(val); - return *this; -} - -RegisterObject& RegisterObject::addDocumentationURL(std::string url) -{ - m_objectRegistrationdata.addDocumentationURL(url); - return *this; -} - -RegisterObject& RegisterObject::addCreator(std::string classname, std::string templatename, - ObjectFactory::BaseObjectCreator::SPtr creator) -{ - m_objectRegistrationdata.addCreator(classname, templatename, creator); - return *this; -} - -RegisterObject::operator int() const -{ - dmsg_warning("RegisterObject") << m_objectRegistrationdata.entry.className - << ": Implicit object registration is deprecated since v24.12. Check #4429 for more information."; - return commitTo(ObjectFactory::getInstance()); -} - -int RegisterObject::commitTo(ObjectFactory* factory) const -{ - return (m_objectRegistrationdata.commitTo(factory) ? 1 : 0); -} - -} // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactory.h b/Sofa/framework/Core/src/sofa/core/ObjectFactory.h index d297f11a72f..16b34881a49 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactory.h @@ -21,408 +21,13 @@ ******************************************************************************/ #pragma once -#include - -#include -#include -#include -#include -#include - - -namespace sofa::helper::system -{ - class Plugin; -} +#include namespace sofa::core { -class ObjectRegistrationData; - -typedef std::function OnCreateCallback ; - -/** - * \brief Main class used to register and dynamically create objects - * - * It uses the Factory design pattern, where each class is registered in a map - * and dynamically retrieved given the type name. - * - * It also stores metainformation on each class, such as description, - * authors, license, and available template types. - */ -class SOFA_CORE_API ObjectFactory -{ -public: - - /** - * Abstract interface used to create instances (object) of a given type - * See the derived class @ref ObjectCreator. - */ - class SOFA_CORE_API BaseObjectCreator - { - public: - using SPtr = std::shared_ptr; - - virtual ~BaseObjectCreator() = default; - - /// Pre-construction check. - /// - /// \return true if the object can be created successfully. - virtual bool canCreate(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) = 0; - - /// Construction method called by the factory. - /// - /// \pre canCreate(context, arg) == true. - virtual objectmodel::BaseComponent::SPtr createInstance(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) = 0; - - /// type_info structure associated with the type of instantiated objects. - virtual const std::type_info& type() = 0; - - /// BaseClass structure associated with the type of instantiated objects. - virtual const objectmodel::BaseClass* getClass() = 0; - - /// The name of the library or executable containing the binary code for this component - virtual const char* getTarget() = 0; - - virtual const char* getHeaderFileLocation() = 0; - }; - using Creator SOFA_CORE_DEPRECATED_RENAME_CREATOR_BASEOBJECTCREATOR() = BaseObjectCreator; - - using TemplateName = std::string; - - /// For a given templated class, the map stores all creators and the key is the template name. - using ObjectTemplateCreatorMap = std::map; - - using CreatorMap SOFA_CORE_DEPRECATED_RENAME_CREATORMAP_OBJECTTEMPLATECREATORMAP() = ObjectTemplateCreatorMap; - - /// Record storing information about a class - class ClassEntry - { - public: - using SPtr = std::shared_ptr; - - std::string className; - std::set aliases; - std::string description; - std::string authors; - std::string license; - std::string documentationURL; - std::string defaultTemplate; - ObjectTemplateCreatorMap creatorMap; // to create instances of the class for different templates - std::map> m_dataAlias ; - }; - - using ClassName = std::string; - - /// Map to store all class entries, key is the class name. - using ClassEntryMap = std::map; - -protected: - - /// Main registry of all classes - ClassEntryMap registry; - - OnCreateCallback m_callbackOnCreate ; - - /// Keep track of plugins who already registered - using RegisteredPluginSet = std::set; - RegisteredPluginSet m_registeredPluginSet; - -public: - - ~ObjectFactory(); - - /// Get an entry given a class name (or alias) - ClassEntry& getEntry(std::string classname); - - /// Test if a creator exists for a given classname - bool hasCreator(std::string classname); - - /// Return the shortname for this classname. Empty string if - /// no creator exists for this classname. - std::string shortName(std::string classname); - - /// Fill the given vector with all the registered classes - void getAllEntries(std::vector& result, bool filterUnloadedPlugins = true); - - /// Fill the given vector with the registered classes from a given target - void getEntriesFromTarget(std::vector& result, std::string target); - - /// Return the list of classes from a given target - std::string listClassesFromTarget(std::string target, std::string separator = ", "); - - /// Fill the given vector with all the registered classes derived from BaseClass - template - void getEntriesDerivedFrom(std::vector& result) const; - - /// Return the list of classes derived from BaseClass as a string - template - std::string listClassesDerivedFrom(const std::string& separator = ", ") const; - - /// Add an alias name for an already registered class - /// - /// \param name name of the new alias - /// \param target class pointed to by the new alias - /// \param force set to true if this method should override any entry already registered for this name - /// \param previous (output) previous ClassEntry registered for this name - bool addAlias(std::string name, std::string target, bool force=false, - ClassEntry::SPtr* previous = nullptr); - - /// Reset an alias to a previous state - /// - /// \param name name of the new alias - /// \param previous previous ClassEntry that need to be registered back for this name - void resetAlias(std::string name, ClassEntry::SPtr previous); - - /// Create an object given a context and a description. - objectmodel::BaseComponent::SPtr createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - - /// Get the ObjectFactory singleton instance - static ObjectFactory* getInstance(); - - /// \copydoc createObject - static objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) - { - return getInstance()->createObject(context, arg); - } - - /// \copydoc addAlias - static bool AddAlias(std::string name, std::string result, bool force=false, - ClassEntry::SPtr* previous = nullptr) - { - return getInstance()->addAlias(name, result, force, previous); - } - - /// \copydoc resetAlias - static void ResetAlias(std::string name, ClassEntry::SPtr previous) - { - getInstance()->resetAlias(name, previous); - } - - /// \copydoc hasCreator - static bool HasCreator(std::string classname) - { - return getInstance()->hasCreator(classname); - } - - static std::string ShortName(std::string classname) - { - return getInstance()->shortName(classname); - } - - /// Dump the content of the factory to a text stream. - void dump(std::ostream& out = std::cout); - - /// Dump the content of the factory to a XML stream. - void dumpXML(std::ostream& out = std::cout); - - /// Dump the content of the factory to a HTML stream. - void dumpHTML(std::ostream& out = std::cout); - - void setCallback(OnCreateCallback cb) { m_callbackOnCreate = cb ; } - - bool registerObjectsFromPlugin(const std::string& pluginName); - bool registerObjects(ObjectRegistrationData& ro); - -}; - -template -void ObjectFactory::getEntriesDerivedFrom(std::vector& result) const -{ - result.clear(); - for (const auto& [mapKeyClassName, entryInRegistry] : registry) - { - // Discard the entry if its class name is not consistent with its key in the map. - // Differences happen for class aliases. - if (entryInRegistry->className == mapKeyClassName) - { - const auto& templateCreators = entryInRegistry->creatorMap; - const auto isAnyInstantiationDerived = std::any_of(templateCreators.begin(), templateCreators.end(), - [](const auto& it) - { - const auto& templateInstantiation = it.second; - const auto* instantiationClass = templateInstantiation->getClass(); - return instantiationClass - && instantiationClass->hasParent(BaseClass::GetClass()); - }); - if (isAnyInstantiationDerived) //at least one template instantiation of the class is derived from BaseClass - { - result.push_back(entryInRegistry); - } - } - } -} - -template -std::string ObjectFactory::listClassesDerivedFrom(const std::string& separator) const -{ - std::vector entries; - getEntriesDerivedFrom(entries); - - return sofa::helper::join(entries.begin(), entries.end(), - [](const ClassEntry::SPtr& entry){ return entry->className;}, separator); -} - -/** - * \brief Typed Creator class used to create instances of object type RealObject - */ -template -class ObjectCreator : public ObjectFactory::BaseObjectCreator -{ -public: - bool canCreate(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) override - { - RealObject* instance = nullptr; - return RealObject::canCreate(instance, context, arg); - } - objectmodel::BaseComponent::SPtr createInstance(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) override - { - RealObject* instance = nullptr; - return RealObject::create(instance, context, arg); - } - const std::type_info& type() override - { - return typeid(RealObject); - } - const objectmodel::BaseClass* getClass() override - { - return RealObject::GetClass(); - } - /// The name of the library or executable containing the binary code for this component - const char* getTarget() override - { -#ifdef SOFA_TARGET - return sofa_tostring(SOFA_TARGET); -#else - return ""; -#endif - } - - const char* getHeaderFileLocation() override - { - return RealObject::HeaderFileLocation(); - } -}; - -/** - * \brief Helper class used to register a class in the ObjectFactory. - * - * This class accumulate information about a given class, as well as creators - * for each supported template instantiation, to register a new entry in - * the ObjectFactory. - * - * It should be used as a temporary object, finalized when used to initialize - * an int static variable. For example : - * \code - * int Fluid3DClass = core::RegisterObject("Eulerian 3D fluid") - * .add\< Fluid3D \>() - * .addLicense("LGPL") - * ; - * \endcode - * - */ -class SOFA_CORE_API ObjectRegistrationData -{ -protected: - /// Class entry being constructed - ObjectFactory::ClassEntry entry; - -public: - - /// Start the registration by giving the description of this class. - explicit ObjectRegistrationData(const std::string& description); - - /// Add an alias name for this class - ObjectRegistrationData& addAlias(std::string val); - - /// Add more descriptive text about this class - ObjectRegistrationData& addDescription(std::string val); - - /// Specify a list of authors (separated with spaces) - ObjectRegistrationData& addAuthor(std::string val); - - /// Specify a license (LGPL, GPL, ...) - ObjectRegistrationData& addLicense(std::string val); - - /// Specify a documentation URL - ObjectRegistrationData& addDocumentationURL(std::string url); - - /// Add a creator able to instance this class with the given templatename. - /// - /// See the add() method for an easy way to add a Creator. - ObjectRegistrationData& addCreator(std::string classname, std::string templatename, - ObjectFactory::BaseObjectCreator::SPtr creator); - - /// Add a template instantiation of this class. - /// - /// \param defaultTemplate set to true if this should be the default instance when no template name is given. - template - ObjectRegistrationData& add(bool defaultTemplate=false) - { - const std::string classname = sofa::core::objectmodel::BaseClassNameHelper::getClassName(); - const std::string templatename = sofa::core::objectmodel::BaseClassNameHelper::getTemplateName(); - - if (defaultTemplate) - entry.defaultTemplate = templatename; - - if (entry.documentationURL.empty()) - { - const std::string target = sofa_tostring(SOFA_TARGET); - const auto modulePaths = sofa::helper::split(target, '.'); - if (modulePaths.size() > 2 && modulePaths[0] == "Sofa" && modulePaths[1] == "Component") - { - entry.documentationURL = std::string(sofa::SOFA_DOCUMENTATION_URL) + std::string("components/"); - entry.documentationURL += sofa::helper::join(modulePaths.begin() + 2, modulePaths.end(), - [](const std::string& m){ return sofa::helper::downcaseString(m);}, "/"); - entry.documentationURL += std::string("/") + sofa::helper::downcaseString(classname); - } - } - - auto objectCreator = std::make_shared >(); - if (strcmp(objectCreator->getTarget(), "") == 0) - { - dmsg_warning("ObjectFactory") << "Module name cannot be found when registering " - << RealObject::GetClass()->className << "<" << RealObject::GetClass()->templateName << "> into the object factory"; - } - return addCreator(classname, templatename, objectCreator); - } - - /// This is the final operation that will actually commit the additions to the ObjectFactory. - bool commitTo(sofa::core::ObjectFactory* objectFactory) const; - - friend class RegisterObject; -}; - - -// Legacy structure, to keep compatibility with olden code -// using the singleton to get the instance of ObjectFactory -class SOFA_ATTRIBUTE_DEPRECATED__REGISTEROBJECT() SOFA_CORE_API RegisterObject -{ -private: - ObjectRegistrationData m_objectRegistrationdata; - -public: - explicit RegisterObject(const std::string& description); - - RegisterObject& addAlias(std::string val); - RegisterObject& addDescription(std::string val); - RegisterObject& addAuthor(std::string val); - RegisterObject& addLicense(std::string val); - RegisterObject& addDocumentationURL(std::string url); - RegisterObject& addCreator(std::string classname, std::string templatename, - ObjectFactory::BaseObjectCreator::SPtr creator); - - template - RegisterObject& add(bool defaultTemplate = false) - { - m_objectRegistrationdata.add(defaultTemplate); - return *this; - } - - operator int() const; +using ObjectFactory = ComponentFactory; +using ObjectRegistrationData = ComponentRegistrationData; - int commitTo(ObjectFactory* factory) const; -}; } // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp index 348a006831f..cb623e830f3 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp @@ -1,157 +1,157 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include -#include - - -namespace sofa::core -{ - -namespace objectmodel -{ - -inline void to_json(nlohmann::json& json, - const objectmodel::BaseClass& baseClass) -{ - json["namespaceName"] = baseClass.namespaceName; - json["typeName"] = baseClass.typeName; - json["className"] = baseClass.className; - json["templateName"] = baseClass.templateName; - json["shortName"] = baseClass.shortName; - - sofa::type::vector parents; - parents.reserve(baseClass.parents.size()); - for (const auto* parent : baseClass.parents) - { - if (parent) - { - parents.push_back(parent->typeName); - } - } - json["parents"] = parents; - - std::vector categories; - sofa::core::CategoryLibrary::getCategories(&baseClass, categories); - json["categories"] = categories; -} - -inline void to_json(nlohmann::json& json, - const objectmodel::BaseData* data) -{ - if (data) - { - json["name"] = data->m_name; - json["group"] = data->group; - json["help"] = data->help; - json["type"] = data->getValueTypeString(); - json["defaultValue"] = data->getDefaultValueString(); - } -} - -inline void to_json(nlohmann::json& json, - const objectmodel::BaseLink* link) -{ - if (link) - { - json["name"] = link->getName(); - json["help"] = link->getHelp(); - json["destinationTypeName"] = link->getValueTypeString(); - } -} - -inline void to_json(nlohmann::json& json, - const objectmodel::BaseComponent::SPtr& object) -{ - if (object) - { - json["data"] = object->getDataFields(); - json["link"] = object->getLinks(); - } -} - -} - -inline void to_json(nlohmann::json& json, - const sofa::core::ObjectFactory::BaseObjectCreator::SPtr& creator) -{ - if (creator) - { - if (const char* target = creator->getTarget()) - { - json["target"] = target; - } - else - { - json["target"] = "targetCannotBeFound"; - } - json["class"] = *creator->getClass(); - - sofa::core::objectmodel::BaseObjectDescription desc; - if (const auto object = creator->createInstance(nullptr, &desc)) - { - json["object"] = object; - } - } -} - -inline void to_json(nlohmann::json& json, - const sofa::core::ObjectFactory::ClassEntry::SPtr& entry) -{ - if (entry) - { - json["className"] = entry->className; - json["description"] = entry->description; - - json["creator"] = entry->creatorMap; - } -} - -std::string ObjectFactoryJson::dump(ObjectFactory* factory) -{ - if (!factory) - { - msg_error("ObjectFactoryJson") << "Invalid factory: cannot dump to json"; - return {}; - } - - std::vector entries; - factory->getAllEntries(entries, true); - - const nlohmann::json json = entries; - - std::string dump{}; - - try - { - dump = json.dump(); - } - catch (const nlohmann::json::type_error& e) - { - msg_error("ObjectFactoryJson") << "Error while dumping json from the object factory: " << e.what(); - dump = json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); - } - - return dump; -} -} +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// #include +// #include +// #include +// #include +// +// +// namespace sofa::core +// { +// +// namespace objectmodel +// { +// +// inline void to_json(nlohmann::json& json, +// const objectmodel::BaseClass& baseClass) +// { +// json["namespaceName"] = baseClass.namespaceName; +// json["typeName"] = baseClass.typeName; +// json["className"] = baseClass.className; +// json["templateName"] = baseClass.templateName; +// json["shortName"] = baseClass.shortName; +// +// sofa::type::vector parents; +// parents.reserve(baseClass.parents.size()); +// for (const auto* parent : baseClass.parents) +// { +// if (parent) +// { +// parents.push_back(parent->typeName); +// } +// } +// json["parents"] = parents; +// +// std::vector categories; +// sofa::core::CategoryLibrary::getCategories(&baseClass, categories); +// json["categories"] = categories; +// } +// +// inline void to_json(nlohmann::json& json, +// const objectmodel::BaseData* data) +// { +// if (data) +// { +// json["name"] = data->m_name; +// json["group"] = data->group; +// json["help"] = data->help; +// json["type"] = data->getValueTypeString(); +// json["defaultValue"] = data->getDefaultValueString(); +// } +// } +// +// inline void to_json(nlohmann::json& json, +// const objectmodel::BaseLink* link) +// { +// if (link) +// { +// json["name"] = link->getName(); +// json["help"] = link->getHelp(); +// json["destinationTypeName"] = link->getValueTypeString(); +// } +// } +// +// inline void to_json(nlohmann::json& json, +// const objectmodel::BaseComponent::SPtr& object) +// { +// if (object) +// { +// json["data"] = object->getDataFields(); +// json["link"] = object->getLinks(); +// } +// } +// +// } +// +// inline void to_json(nlohmann::json& json, +// const sofa::core::ObjectFactory::BaseObjectCreator::SPtr& creator) +// { +// if (creator) +// { +// if (const char* target = creator->getTarget()) +// { +// json["target"] = target; +// } +// else +// { +// json["target"] = "targetCannotBeFound"; +// } +// json["class"] = *creator->getClass(); +// +// sofa::core::objectmodel::BaseObjectDescription desc; +// if (const auto object = creator->createInstance(nullptr, &desc)) +// { +// json["object"] = object; +// } +// } +// } +// +// inline void to_json(nlohmann::json& json, +// const sofa::core::ObjectFactory::ClassEntry::SPtr& entry) +// { +// if (entry) +// { +// json["className"] = entry->className; +// json["description"] = entry->description; +// +// json["creator"] = entry->creatorMap; +// } +// } +// +// std::string ObjectFactoryJson::dump(ObjectFactory* factory) +// { +// if (!factory) +// { +// msg_error("ObjectFactoryJson") << "Invalid factory: cannot dump to json"; +// return {}; +// } +// +// std::vector entries; +// factory->getAllEntries(entries, true); +// +// const nlohmann::json json = entries; +// +// std::string dump{}; +// +// try +// { +// dump = json.dump(); +// } +// catch (const nlohmann::json::type_error& e) +// { +// msg_error("ObjectFactoryJson") << "Error while dumping json from the object factory: " << e.what(); +// dump = json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); +// } +// +// return dump; +// } +// } diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h index 393f75ab778..9fd4cba4e97 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h @@ -26,10 +26,10 @@ namespace sofa::core { -class ObjectFactory; -struct SOFA_CORE_API ObjectFactoryJson -{ - static std::string dump(ObjectFactory* factory); -}; +// class ObjectFactory; +// struct SOFA_CORE_API ObjectFactoryJson +// { +// static std::string dump(ObjectFactory* factory); +// }; } diff --git a/Sofa/framework/Core/src/sofa/core/SofaLibrary.cpp b/Sofa/framework/Core/src/sofa/core/SofaLibrary.cpp index 0b29730050e..79f69853b3f 100644 --- a/Sofa/framework/Core/src/sofa/core/SofaLibrary.cpp +++ b/Sofa/framework/Core/src/sofa/core/SofaLibrary.cpp @@ -1,153 +1,153 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include - - -namespace sofa::core -{ - -//Automatically create and destroy all the components available: easy way to verify the default constructor and destructor -void SofaLibrary::build( const std::vector< std::string >& examples) -{ - exampleFiles=examples; - //----------------------------------------------------------------------- - //Read the content of the Object Factory - //----------------------------------------------------------------------- - std::vector entries; - sofa::core::ObjectFactory::getInstance()->getAllEntries(entries); - //Set of categories found in the Object Factory - std::set< std::string > mainCategories; - //Data containing all the entries for a given category - std::multimap< std::string, ClassEntry::SPtr> inventory; - - for (std::size_t i=0; icreatorMap.begin(); - if (creatorEntry != entries[i]->creatorMap.end()) - { - const objectmodel::BaseClass* baseClass = creatorEntry->second->getClass(); - std::vector categoriesVec; - CategoryLibrary::getCategories(baseClass, categoriesVec); - for (std::vector::iterator it = categoriesVec.begin(); it != categoriesVec.end(); ++it) - { - mainCategories.insert((*it)); - inventory.insert(std::make_pair((*it), entries[i])); - } - } - } - - //----------------------------------------------------------------------- - //Using the inventory, Add each component to the Sofa Library - //----------------------------------------------------------------------- - std::set< std::string >::iterator itCategory; - typedef std::multimap< std::string, ClassEntry::SPtr >::iterator IteratorInventory; - - - //We add the components category by category - for (itCategory = mainCategories.begin(); itCategory != mainCategories.end(); ++itCategory) - { - const std::string& categoryName = *itCategory; - IteratorInventory itComponent; - - std::pair< IteratorInventory,IteratorInventory > rangeCategory; - rangeCategory = inventory.equal_range(categoryName); - - - - const unsigned int numComponentInCategory = (unsigned int)inventory.count(categoryName); - CategoryLibrary *category = createCategory(categoryName,numComponentInCategory); - - //Process all the component of the current category, and add them to the group - for (itComponent=rangeCategory.first; itComponent != rangeCategory.second; ++itComponent) - { - const ClassEntry::SPtr entry = itComponent->second; - const std::string &componentName=entry->className; - - //Add the component to the category - category->addComponent(componentName, entry, exampleFiles); - } - category->endConstruction(); - addCategory(category); - } - computeNumComponents(); -} - -void SofaLibrary::computeNumComponents() -{ - numComponents=0; - for (std::size_t cat=0; catgetNumComponents(); - } - -} - -void SofaLibrary::addCategory(CategoryLibrary *category) -{ - categories.push_back(category); -} - - -std::string SofaLibrary::getComponentDescription( const std::string &componentName ) const -{ - const ComponentLibrary *component = getComponent(componentName); - if (component) return component->getDescription(); - else return ""; -} - -const CategoryLibrary *SofaLibrary::getCategory( const std::string &categoryName) const -{ - for (VecCategoryIterator it=categories.begin(); it != categories.end(); ++it) - { - if ((*it)->getName().find(categoryName) != std::string::npos) - return *it; - } - return nullptr; -} - -const ComponentLibrary *SofaLibrary::getComponent( const std::string &componentName ) const -{ - //Look into all the categories - for (std::size_t cat=0; cat &components = categories[cat]->getComponents(); - for (std::size_t comp=0; compgetName()) return components[comp]; - } - } - return nullptr; -} - -void SofaLibrary::clear() -{ - for (std::size_t i=0; i. * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// +// #include +// #include +// +// +// namespace sofa::core +// { +// +// //Automatically create and destroy all the components available: easy way to verify the default constructor and destructor +// void SofaLibrary::build( const std::vector< std::string >& examples) +// { +// exampleFiles=examples; +// //----------------------------------------------------------------------- +// //Read the content of the Object Factory +// //----------------------------------------------------------------------- +// std::vector entries; +// sofa::core::ObjectFactory::getInstance()->getAllEntries(entries); +// //Set of categories found in the Object Factory +// std::set< std::string > mainCategories; +// //Data containing all the entries for a given category +// std::multimap< std::string, ClassEntry::SPtr> inventory; +// +// for (std::size_t i=0; icreatorMap.begin(); +// if (creatorEntry != entries[i]->creatorMap.end()) +// { +// const objectmodel::BaseClass* baseClass = creatorEntry->second->getClass(); +// std::vector categoriesVec; +// CategoryLibrary::getCategories(baseClass, categoriesVec); +// for (std::vector::iterator it = categoriesVec.begin(); it != categoriesVec.end(); ++it) +// { +// mainCategories.insert((*it)); +// inventory.insert(std::make_pair((*it), entries[i])); +// } +// } +// } +// +// //----------------------------------------------------------------------- +// //Using the inventory, Add each component to the Sofa Library +// //----------------------------------------------------------------------- +// std::set< std::string >::iterator itCategory; +// typedef std::multimap< std::string, ClassEntry::SPtr >::iterator IteratorInventory; +// +// +// //We add the components category by category +// for (itCategory = mainCategories.begin(); itCategory != mainCategories.end(); ++itCategory) +// { +// const std::string& categoryName = *itCategory; +// IteratorInventory itComponent; +// +// std::pair< IteratorInventory,IteratorInventory > rangeCategory; +// rangeCategory = inventory.equal_range(categoryName); +// +// +// +// const unsigned int numComponentInCategory = (unsigned int)inventory.count(categoryName); +// CategoryLibrary *category = createCategory(categoryName,numComponentInCategory); +// +// //Process all the component of the current category, and add them to the group +// for (itComponent=rangeCategory.first; itComponent != rangeCategory.second; ++itComponent) +// { +// const ClassEntry::SPtr entry = itComponent->second; +// const std::string &componentName=entry->className; +// +// //Add the component to the category +// category->addComponent(componentName, entry, exampleFiles); +// } +// category->endConstruction(); +// addCategory(category); +// } +// computeNumComponents(); +// } +// +// void SofaLibrary::computeNumComponents() +// { +// numComponents=0; +// for (std::size_t cat=0; catgetNumComponents(); +// } +// +// } +// +// void SofaLibrary::addCategory(CategoryLibrary *category) +// { +// categories.push_back(category); +// } +// +// +// std::string SofaLibrary::getComponentDescription( const std::string &componentName ) const +// { +// const ComponentLibrary *component = getComponent(componentName); +// if (component) return component->getDescription(); +// else return ""; +// } +// +// const CategoryLibrary *SofaLibrary::getCategory( const std::string &categoryName) const +// { +// for (VecCategoryIterator it=categories.begin(); it != categories.end(); ++it) +// { +// if ((*it)->getName().find(categoryName) != std::string::npos) +// return *it; +// } +// return nullptr; +// } +// +// const ComponentLibrary *SofaLibrary::getComponent( const std::string &componentName ) const +// { +// //Look into all the categories +// for (std::size_t cat=0; cat &components = categories[cat]->getComponents(); +// for (std::size_t comp=0; compgetName()) return components[comp]; +// } +// } +// return nullptr; +// } +// +// void SofaLibrary::clear() +// { +// for (std::size_t i=0; i. * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -namespace sofa::core -{ - - -/** - * \brief An Generic Library - * - * It reads the content of the Object Factory and builds a library of components sorted inside categories. - * This Interface is used for the Modeler mainly. - * - */ -class SOFA_CORE_API SofaLibrary -{ -public: - typedef std::vector< CategoryLibrary* > VecCategory; - typedef VecCategory::const_iterator VecCategoryIterator; - - virtual ~SofaLibrary() {} - - virtual void build(const std::vector< std::string >& examples=std::vector< std::string >()); - virtual void clear(); - - std::string getComponentDescription( const std::string &componentName) const; - - const VecCategory& getCategories() const {return categories;}; - - const CategoryLibrary *getCategory( const std::string &categoryName ) const; - const ComponentLibrary *getComponent( const std::string &componentName) const; - unsigned int getNumComponents() const {return numComponents;} - -protected: - virtual CategoryLibrary *createCategory(const std::string &category, - unsigned int /* numCategory */) { - return new CategoryLibrary(category); - } - virtual void addCategory(CategoryLibrary *); - void computeNumComponents(); - - VecCategory categories; - std::vector< std::string > exampleFiles; - unsigned int numComponents; - -}; - -} +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// #pragma once +// +// #include +// +// namespace sofa::core +// { +// +// +// /** +// * \brief An Generic Library +// * +// * It reads the content of the Object Factory and builds a library of components sorted inside categories. +// * This Interface is used for the Modeler mainly. +// * +// */ +// class SOFA_CORE_API SofaLibrary +// { +// public: +// typedef std::vector< CategoryLibrary* > VecCategory; +// typedef VecCategory::const_iterator VecCategoryIterator; +// +// virtual ~SofaLibrary() {} +// +// virtual void build(const std::vector< std::string >& examples=std::vector< std::string >()); +// virtual void clear(); +// +// std::string getComponentDescription( const std::string &componentName) const; +// +// const VecCategory& getCategories() const {return categories;}; +// +// const CategoryLibrary *getCategory( const std::string &categoryName ) const; +// const ComponentLibrary *getComponent( const std::string &componentName) const; +// unsigned int getNumComponents() const {return numComponents;} +// +// protected: +// virtual CategoryLibrary *createCategory(const std::string &category, +// unsigned int /* numCategory */) { +// return new CategoryLibrary(category); +// } +// virtual void addCategory(CategoryLibrary *); +// void computeNumComponents(); +// +// VecCategory categories; +// std::vector< std::string > exampleFiles; +// unsigned int numComponents; +// +// }; +// +// } diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/DefaultAnimationLoop.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/DefaultAnimationLoop.cpp index 605062dedf9..fb920a31b36 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/DefaultAnimationLoop.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/DefaultAnimationLoop.cpp @@ -51,7 +51,7 @@ #include #include #include - +#include namespace sofa::simulation { diff --git a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckMissingRequiredPlugin.cpp b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckMissingRequiredPlugin.cpp index ffa39e73864..27d905fbda2 100644 --- a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckMissingRequiredPlugin.cpp +++ b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckMissingRequiredPlugin.cpp @@ -51,32 +51,32 @@ const std::string SceneCheckMissingRequiredPlugin::getDesc() void SceneCheckMissingRequiredPlugin::doCheckOn(sofa::simulation::Node* node) { - for (const auto& object : node->object) - { - const ObjectFactory::ClassEntry entry = ObjectFactory::getInstance()->getEntry(object->getClassName()); - if(!entry.creatorMap.empty()) - { - ObjectFactory::ObjectTemplateCreatorMap::const_iterator it = entry.creatorMap.find(object->getTemplateName()); - if(entry.creatorMap.end() != it && *it->second->getTarget()) - { - const std::string pluginName = it->second->getTarget(); - const std::string path = PluginManager::getInstance().findPlugin(pluginName); - if( PluginManager::getInstance().pluginIsLoaded(path) - && !m_loadedPlugins.contains(pluginName)) - { - m_requiredPlugins[pluginName].push_back(object->getClassName()); - } - } - } - } - - //sort and remove duplicates - for (auto& plugins : m_requiredPlugins) - { - auto& v = plugins.second; - std::sort(v.begin(), v.end()); - v.erase(std::unique(v.begin(), v.end()), v.end()); - } + // for (const auto& object : node->object) + // { + // const ObjectFactory::ClassEntry entry = ObjectFactory::getInstance()->getEntry(object->getClassName()); + // if(!entry.creatorMap.empty()) + // { + // ObjectFactory::ObjectTemplateCreatorMap::const_iterator it = entry.creatorMap.find(object->getTemplateName()); + // if(entry.creatorMap.end() != it && *it->second->getTarget()) + // { + // const std::string pluginName = it->second->getTarget(); + // const std::string path = PluginManager::getInstance().findPlugin(pluginName); + // if( PluginManager::getInstance().pluginIsLoaded(path) + // && !m_loadedPlugins.contains(pluginName)) + // { + // m_requiredPlugins[pluginName].push_back(object->getClassName()); + // } + // } + // } + // } + // + // //sort and remove duplicates + // for (auto& plugins : m_requiredPlugins) + // { + // auto& v = plugins.second; + // std::sort(v.begin(), v.end()); + // v.erase(std::unique(v.begin(), v.end()), v.end()); + // } } void SceneCheckMissingRequiredPlugin::printSummary(simulation::SceneLoader* sceneLoader) diff --git a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckUsingAlias.cpp b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckUsingAlias.cpp index a94e895a5cc..40d02d4d5f2 100644 --- a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckUsingAlias.cpp +++ b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckUsingAlias.cpp @@ -40,13 +40,13 @@ using sofa::core::ObjectFactory; SceneCheckUsingAlias::SceneCheckUsingAlias() { /// Add a callback to be n - ObjectFactory::getInstance()->setCallback([this](Base* o, BaseObjectDescription *arg) { - const std::string typeNameInScene = arg->getAttribute("type", ""); - if ( typeNameInScene != o->getClassName() ) - { - this->m_componentsCreatedUsingAlias[o->getClassName()].push_back(typeNameInScene); - } - }); + // ObjectFactory::getInstance()->setCallback([this](Base* o, BaseObjectDescription *arg) { + // const std::string typeNameInScene = arg->getAttribute("type", ""); + // if ( typeNameInScene != o->getClassName() ) + // { + // this->m_componentsCreatedUsingAlias[o->getClassName()].push_back(typeNameInScene); + // } + // }); } SceneCheckUsingAlias::~SceneCheckUsingAlias() From 9b9430ca2e59620db1b218a2ebd82c32ebbe50af Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 10:05:16 +0200 Subject: [PATCH 02/71] clean plugin loading --- .../Core/src/sofa/core/ComponentFactory.cpp | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index b3cb77bbfbf..6dd1a83f027 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -156,6 +156,31 @@ bool ComponentFactory::registerObjects(ComponentRegistrationData& ro) } +namespace +{ +void loadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& classname) +{ + // The last dot separates the module name from the component name + // Example: Module.Name.ComponentName (it is common to have dots in the module names) + // It is assumed that the component name does not contain any dot + auto lastDot = classname.find_last_of('.'); + if (lastDot != std::string::npos) + { + const auto pluginName = classname.substr(0, lastDot); + + const auto [path, loaded] = helper::system::PluginManager::getInstance().isPluginLoaded(pluginName); + if (!loaded) + { + auto status = helper::system::PluginManager::getInstance().loadPluginByName(pluginName); + if (status == helper::system::PluginManager::PluginLoadStatus::SUCCESS) + { + self.registerObjectsFromPlugin(pluginName); + } + } + } +} +} + objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { @@ -168,25 +193,10 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; std::string classname {typeAttribute}; + loadPluginIfNameContainsPluginName(*this, classname); auto candidates = this->getComponentsFromName(classname); - if (candidates.empty()) - { - //try to load a potential plugin based on the component name - auto lastDot = classname.find_last_of('.'); - if (lastDot != std::string::npos) - { - auto pluginName = classname.substr(0, lastDot); - auto status = helper::system::PluginManager::getInstance().loadPluginByName(pluginName); - if (status == helper::system::PluginManager::PluginLoadStatus::SUCCESS) - { - this->registerObjectsFromPlugin(pluginName); - candidates = this->getComponentsFromName(classname); - } - } - } - if (candidates.empty()) { return nullptr; From 10dea2173a5218ce70235e589dd2c3a2ed253207 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 10:07:15 +0200 Subject: [PATCH 03/71] cleaning --- .../Core/src/sofa/core/ComponentFactory.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 6dd1a83f027..e3e97b9857f 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -179,6 +179,12 @@ void loadPluginIfNameContainsPluginName(ComponentFactory& self, const std::strin } } } + +void selectCandidates(std::vector& candidates) +{ + //template deduction +} + } objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( @@ -210,15 +216,9 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); }); - for (const auto& component : candidates) - { - - } + selectCandidates(candidates); const auto firstCandidate = candidates.front(); - - firstCandidate->componentModule; - auto component = candidates.front()->creator->create(); if (component) From 3688674442a04d66aee8efea4281e43949102263 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 15:21:52 +0200 Subject: [PATCH 04/71] separate concepts: data/builder/creator --- .../statecontainer/MechanicalObject.cpp | 29 ++- .../Core/src/sofa/core/ComponentFactory.cpp | 166 +++++++++++------- .../Core/src/sofa/core/ComponentFactory.h | 27 ++- .../src/sofa/core/ComponentRegistrationData.h | 154 +++++++--------- .../Core/src/sofa/core/ObjectFactory.h | 2 +- 5 files changed, 203 insertions(+), 175 deletions(-) diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp index 9763be97bde..1badda1bf77 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp @@ -41,15 +41,30 @@ template class SOFA_COMPONENT_STATECONTAINER_API MechanicalObject; template class SOFA_COMPONENT_STATECONTAINER_API MechanicalObject; template class SOFA_COMPONENT_STATECONTAINER_API MechanicalObject; +static constexpr std::string_view description {"Mechanical state vectors"}; + +template +void registerComponent(sofa::core::ComponentFactory* factory) +{ + factory->registerComponent>( + core::ComponentRegistrationDataBuilder() + .setName("MechanicalObject") + .addAlias("State") + .addTemplateAttribute("dofType") + .setDescription(std::string(description)) + .setModuleName(MODULE_NAME) + ); +}; + void registerMechanicalObject(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("mechanical state vectors") - .add< MechanicalObject >(true) // default template - .add< MechanicalObject >() - .add< MechanicalObject >() - .add< MechanicalObject >() - .add< MechanicalObject >() - .add< MechanicalObject >()); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + } template<> diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index e3e97b9857f..9bad5a92356 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -20,6 +20,7 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #include +#include #include #include #include @@ -42,7 +43,7 @@ std::vector ComponentFactory::getComponentsFromName( for (const auto& component : m_registry) { - const auto fullName = component->componentNamespace + "." + component->componentName; + const auto fullName = component->componentModule + "." + component->componentName; if (component->componentName == componentToSearch || fullName == componentToSearch) { @@ -52,7 +53,7 @@ std::vector ComponentFactory::getComponentsFromName( { for (const auto& alias : component->aliases) { - const auto fullNameAlias = component->componentNamespace + "." + alias; + const auto fullNameAlias = component->componentModule + "." + alias; if (alias == componentToSearch || fullNameAlias == componentToSearch) { @@ -109,48 +110,48 @@ bool ComponentFactory::registerObjectsFromPlugin(const std::string& pluginName) } } -bool ComponentFactory::registerObjects(ComponentRegistrationData& ro) +bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) { - auto& creators = ro.creators; - - if (creators.empty()) - { - msg_error() << "No creator provided"; - return false; - } - - for (auto& creator : ro.creators) - { - ComponentDescription::SPtr component = std::make_shared(); - - component->componentName = ro.componentName; - component->aliases = ro.aliases; - component->componentNamespace = ro.componentNamespace; - component->componentModule = ro.componentModule; - - component->description = ro.description; - component->authors = sofa::helper::join(ro.authors, ","); - component->license = ro.license; - component->documentationURL = ro.documentationURL; - - { - //special cases for official documentation - const auto modulePaths = sofa::helper::split(component->componentModule, '.'); - if (modulePaths.size() > 2 && modulePaths[0] == "Sofa" && modulePaths[1] == "Component") - { - std::string officialDocURL = std::string(sofa::SOFA_DOCUMENTATION_URL) + std::string("components/"); - officialDocURL += sofa::helper::join(modulePaths.begin() + 2, modulePaths.end(), - [](const std::string& m){ return sofa::helper::downcaseString(m);}, "/"); - officialDocURL += std::string("/") + sofa::helper::downcaseString(component->componentName); - - component->documentationURL.insert(officialDocURL); - } - } - - component->creator = std::move(creator); - - this->m_registry.push_back(component); - } + // auto& creators = ro.creators; + // + // if (creators.empty()) + // { + // msg_error() << "No creator provided"; + // return false; + // } + // + // for (auto& creator : ro.creators) + // { + // ComponentDescription::SPtr component = std::make_shared(); + // + // component->componentName = ro.componentName; + // component->aliases = ro.aliases; + // component->componentNamespace = ro.componentNamespace; + // component->componentModule = ro.componentModule; + // + // component->description = ro.description; + // component->authors = sofa::helper::join(ro.authors, ","); + // component->license = ro.license; + // component->documentationURL = ro.documentationURL; + // + // { + // //special cases for official documentation + // const auto modulePaths = sofa::helper::split(component->componentModule, '.'); + // if (modulePaths.size() > 2 && modulePaths[0] == "Sofa" && modulePaths[1] == "Component") + // { + // std::string officialDocURL = std::string(sofa::SOFA_DOCUMENTATION_URL) + std::string("components/"); + // officialDocURL += sofa::helper::join(modulePaths.begin() + 2, modulePaths.end(), + // [](const std::string& m){ return sofa::helper::downcaseString(m);}, "/"); + // officialDocURL += std::string("/") + sofa::helper::downcaseString(component->componentName); + // + // component->documentationURL.insert(officialDocURL); + // } + // } + // + // component->creator = std::move(creator); + // + // this->m_registry.push_back(component); + // } return true; @@ -158,7 +159,7 @@ bool ComponentFactory::registerObjects(ComponentRegistrationData& ro) namespace { -void loadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& classname) +void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& classname) { // The last dot separates the module name from the component name // Example: Module.Name.ComponentName (it is common to have dots in the module names) @@ -180,9 +181,54 @@ void loadPluginIfNameContainsPluginName(ComponentFactory& self, const std::strin } } -void selectCandidates(std::vector& candidates) +std::vector selectCandidates(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) +{ + std::vector matchingCandidates; + + for (const auto& candidate : candidates) + { + bool matchAllTemplateParameters = true; + for (const auto& [attribute, value] : candidate->templateAttributes) + { + const char* attr = arg->getAttribute(attribute, nullptr); + if (attr == nullptr) + { + matchAllTemplateParameters = false; + } + else + { + const std::string attrStr { attr }; + if (defaulttype::TemplateAliases::resolveAlias(attrStr) != value) + { + matchAllTemplateParameters = false; + } + } + } + + if (matchAllTemplateParameters) + { + matchingCandidates.push_back(candidate); + } + } + + return matchingCandidates; +} + +auto createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { - //template deduction + auto component = desc->creator->create(); + + if (component) + { + if (context) + { + context->addObject(component); + } + + component->parse(arg); + } + + return component; } } @@ -199,7 +245,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; std::string classname {typeAttribute}; - loadPluginIfNameContainsPluginName(*this, classname); + autoLoadPluginIfNameContainsPluginName(*this, classname); auto candidates = this->getComponentsFromName(classname); @@ -208,30 +254,26 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; } - std::sort(candidates.begin(), candidates.end(), - [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - std::erase_if(candidates, [](const ComponentDescription::SPtr& candidate) { return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); }); - selectCandidates(candidates); + const auto matchingTemplates = selectCandidates(candidates, arg); - const auto firstCandidate = candidates.front(); - auto component = candidates.front()->creator->create(); - - if (component) + if (!matchingTemplates.empty()) { - if (context) - { - context->addObject(component); - } - - component->parse(arg); + msg_warning_when(matchingTemplates.size() > 1) << "Multiple candidates with the same templates"; + return createComponentFrom(matchingTemplates.front(), context, arg); } - return component; + //todo: template deduction + + std::sort(candidates.begin(), candidates.end(), + [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); + + + return createComponentFrom(candidates.front(), context, arg); } bool ComponentFactory::hasCreator(const std::string& classname) const diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 9aa05debcc9..a10beb09410 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -29,22 +29,12 @@ namespace sofa::core { struct SOFA_CORE_API ComponentDescription + : public ComponentRegistrationData { using SPtr = std::shared_ptr; - std::string componentName; - std::set aliases; - - std::set templateAttributes; - std::string componentNamespace; - std::string componentModule; - - unsigned int instantiationPriority {}; - - std::string description; - std::string authors; - std::string license; - std::set documentationURL; + explicit ComponentDescription(const ComponentRegistrationData& data) + : ComponentRegistrationData(data) {} std::unique_ptr creator; }; @@ -59,8 +49,17 @@ class SOFA_CORE_API ComponentFactory std::vector getComponentsFromName(const std::string& componentName) const; bool registerObjectsFromPlugin(const std::string& pluginName); - bool registerObjects(ComponentRegistrationData& ro); + //to deprecate + bool registerObjects(LegacyComponentRegistrationData& ro); + + template + void registerComponent(const ComponentRegistrationData& componentDescription) + { + auto componentInRegistry = std::make_shared(componentDescription); + componentInRegistry->creator = std::make_unique>(); + m_registry.push_back(componentInRegistry); + } /// Create a component given a context and a description. diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 7bb36a8c5fd..83dd1df4f40 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -32,136 +32,108 @@ namespace sofa::core { -class SOFA_CORE_API ComponentRegistrationData +struct SOFA_CORE_API ComponentRegistrationData { -public: - std::string componentName; std::set aliases; - std::string description; + std::unordered_map templateAttributes; + + std::string componentNamespace; + std::string componentModule; + + unsigned int instantiationPriority {}; + + std::string description; std::set authors; std::string license; std::set documentationURL; - std::string componentNamespace; - std::string componentModule; +}; - std::optional defaultTemplateId; +struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistrationData +{ + ComponentRegistrationDataBuilder& setName(const std::string& _componentName) + { + this->componentName = _componentName; + return *this; + } + + ComponentRegistrationDataBuilder& addAlias(const std::string& alias) + { + this->aliases.insert(alias); + return *this; + } - std::vector> creators; + ComponentRegistrationDataBuilder& addTemplateAttribute( + const std::string& templateAttribute, const std::string& value) + { + this->templateAttributes[templateAttribute] = value; + return *this; + } - /// Start the registration by giving the description of this class. - explicit ComponentRegistrationData(const std::string& description) + template + ComponentRegistrationDataBuilder& addTemplateAttribute(const std::string& templateAttribute) { - if (!description.empty()) - { - addDescription(description); - } + return addTemplateAttribute(templateAttribute, T::Name()); } - ComponentRegistrationData(const ComponentRegistrationData&) = delete; - void operator=(const ComponentRegistrationData&) = delete; + ComponentRegistrationDataBuilder& setModuleName(const std::string& _moduleName) + { + this->componentModule = _moduleName; + return *this; + } - /// Add an alias name for this class - ComponentRegistrationData& addAlias(std::string val) + ComponentRegistrationDataBuilder& setDescription(const std::string& _description) { - aliases.insert(val); + this->description = _description; return *this; } - /// Add more descriptive text about this class - ComponentRegistrationData& addDescription(const std::string& val) + ComponentRegistrationDataBuilder& addAuthor(const std::string& _author) { - if (description.empty()) - { - description = val; - } - else - { - dmsg_error("ComponentRegistrationData") << "Trying to add multiple descriptions for a single component whereas only one is supported"; - } + this->authors.insert(_author); return *this; } - /// Specify a list of authors (separated with spaces) - ComponentRegistrationData& addAuthor(std::string val) + ComponentRegistrationDataBuilder& setLicense(const std::string& _license) { - authors.insert(val); + this->license = _license; return *this; } - /// Specify a license (LGPL, GPL, ...) - ComponentRegistrationData& addLicense(std::string val) + ComponentRegistrationDataBuilder& setDocumentationURL(const std::string& _documentationURL) { - if (license.empty()) - { - license = val; - } - else - { - dmsg_error("ComponentRegistrationData") << "Trying to add multiple licenses for a single component whereas only one is supported"; - } + this->documentationURL.insert(_documentationURL); return *this; } +}; + +//to deprecate +struct SOFA_CORE_API LegacyComponentRegistrationData +{ + explicit LegacyComponentRegistrationData(const std::string&) {} - /// Specify a documentation URL - ComponentRegistrationData& addDocumentationURL(std::string url) + template LegacyComponentRegistrationData& add(bool = false) { - documentationURL.insert(url); return *this; } - /// Add a template instantiation of this class. - /// - /// \param defaultTemplate set to true if this should be the default instance when no template name is given. - template - ComponentRegistrationData& add(bool defaultTemplate = false) + LegacyComponentRegistrationData& addDocumentationURL(const std::string&) { -#ifdef SOFA_TARGET - const std::string target = sofa_tostring(SOFA_TARGET); - - if (!target.empty()) - { - componentNamespace = target; - componentModule = target; - } -#else - dmsg_warning("ComponentFactory") << "Module name cannot be found when registering " - << RealObject::GetClass()->className << "<" << RealObject::GetClass()->templateName << "> into the component factory"; -#endif - - const std::string classname = sofa::core::objectmodel::BaseClassNameHelper::getClassName(); - if (componentName.empty()) - { - componentName = classname; - } - else - { - if (componentName != classname) - { - msg_error("ComponentFactory") << "Trying to define a class (" << classname << ") unrelated to " << componentName; - return *this; - } - } - - creators.push_back(std::unique_ptr(new ComponentCreator)); - - if (defaultTemplate) - { - if (defaultTemplateId.has_value()) - { - msg_error("ComponentFactory") << "Trying to define a default template for " - << RealObject::GetClass()->className << " whereas one was already defined before"; - } - else - { - defaultTemplateId = creators.size() - 1; - } - } + return *this; + } + LegacyComponentRegistrationData& addDescription(const std::string&) + { return *this; } + LegacyComponentRegistrationData& addAlias(const std::string&) + { + return *this; + } }; + + } diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactory.h b/Sofa/framework/Core/src/sofa/core/ObjectFactory.h index 16b34881a49..f5fa1460c8a 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactory.h @@ -27,7 +27,7 @@ namespace sofa::core { using ObjectFactory = ComponentFactory; -using ObjectRegistrationData = ComponentRegistrationData; +using ObjectRegistrationData = LegacyComponentRegistrationData; } // namespace sofa::core From 2dde2d8ba02653f2589887520502db2c6b9ee9e6 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 15:47:45 +0200 Subject: [PATCH 05/71] introduce deduction rules --- .../Core/src/sofa/core/ComponentFactory.cpp | 40 ++++++++++++++++--- .../src/sofa/core/ComponentRegistrationData.h | 9 +++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 9bad5a92356..9e3dc5bb614 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -181,7 +181,7 @@ void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::s } } -std::vector selectCandidates(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) +std::vector selectCandidatesTemplateAttributes(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) { std::vector matchingCandidates; @@ -214,6 +214,25 @@ std::vector selectCandidates(const std::vector selectCandidatesDeductionRules( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) +{ + std::vector matchingCandidates; + + for (const auto& candidate : candidates) + { + if (auto rule = candidate->templateDeductionRule; + rule->doesTemplateDeductionApply(context, arg)) + { + matchingCandidates.push_back(candidate); + } + } + + return matchingCandidates; +} + auto createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { auto component = desc->creator->create(); @@ -259,7 +278,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); }); - const auto matchingTemplates = selectCandidates(candidates, arg); + const auto matchingTemplates = selectCandidatesTemplateAttributes(candidates, arg); if (!matchingTemplates.empty()) { @@ -267,13 +286,22 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return createComponentFrom(matchingTemplates.front(), context, arg); } - //todo: template deduction + //todo: selection based on the legacy 'template' keyword - std::sort(candidates.begin(), candidates.end(), - [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); + //template deduction + // There are candidates, but none of them match the templates + // We select one of them automatically based on deduction rules + const auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); + if (!deducedCandidates.empty()) + { + return nullptr; + } + + std::sort(deducedCandidates.begin(), deducedCandidates.end(), + [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - return createComponentFrom(candidates.front(), context, arg); + return createComponentFrom(deducedCandidates.front(), context, arg); } bool ComponentFactory::hasCreator(const std::string& classname) const diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 83dd1df4f40..191721f2e9e 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -32,6 +32,13 @@ namespace sofa::core { +struct SOFA_CORE_API BaseTemplateDeductionRule +{ + virtual bool doesTemplateDeductionApply( + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) = 0; +}; + struct SOFA_CORE_API ComponentRegistrationData { std::string componentName; @@ -48,6 +55,8 @@ struct SOFA_CORE_API ComponentRegistrationData std::set authors; std::string license; std::set documentationURL; + + std::shared_ptr templateDeductionRule; }; struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistrationData From 19990bac68810e739bcacb3a70da42e54d67d65b Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 16:02:09 +0200 Subject: [PATCH 06/71] legacy template keyword --- .../Core/src/sofa/core/ComponentFactory.cpp | 50 +++++++++++++++---- .../src/sofa/core/ComponentRegistrationData.h | 4 +- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 9e3dc5bb614..7f2506273b3 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -214,6 +214,33 @@ std::vector selectCandidatesTemplateAttributes(const return matchingCandidates; } +std::vector selectCandidatesTemplateKeyword( + const std::vector& candidates, + objectmodel::BaseObjectDescription* arg) +{ + const char* templateAttr = arg->getAttribute("template", nullptr); + if (!templateAttr) + return {}; + + std::string templateAttrStr { templateAttr }; + templateAttrStr = defaulttype::TemplateAliases::resolveAlias(templateAttrStr); + + std::vector matchingCandidates; + + for (const auto& candidate : candidates) + { + const auto templateList = sofa::helper::join( + candidate->templateAttributes.begin(), candidate->templateAttributes.end(), + [](const auto& attr){ return attr.second; }, ','); + if (templateAttrStr == templateList) + { + matchingCandidates.push_back(candidate); + } + } + + return matchingCandidates; +} + std::vector selectCandidatesDeductionRules( const std::vector& candidates, objectmodel::BaseContext* context, @@ -223,8 +250,8 @@ std::vector selectCandidatesDeductionRules( for (const auto& candidate : candidates) { - if (auto rule = candidate->templateDeductionRule; - rule->doesTemplateDeductionApply(context, arg)) + if (const auto rule = candidate->templateDeductionRule; + rule && rule->doesTemplateDeductionApply(context, arg)) { matchingCandidates.push_back(candidate); } @@ -278,6 +305,9 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); }); + std::sort(candidates.begin(), candidates.end(), + [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); + const auto matchingTemplates = selectCandidatesTemplateAttributes(candidates, arg); if (!matchingTemplates.empty()) @@ -286,22 +316,24 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return createComponentFrom(matchingTemplates.front(), context, arg); } - //todo: selection based on the legacy 'template' keyword + // Selection based on the legacy 'template' keyword + const auto templateCandidates = selectCandidatesTemplateKeyword(candidates, arg); + if (!templateCandidates.empty()) + { + return createComponentFrom(templateCandidates.front(), context, arg); + } //template deduction // There are candidates, but none of them match the templates // We select one of them automatically based on deduction rules - const auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); + auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); if (!deducedCandidates.empty()) { - return nullptr; + return createComponentFrom(deducedCandidates.front(), context, arg); } - std::sort(deducedCandidates.begin(), deducedCandidates.end(), - [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - - return createComponentFrom(deducedCandidates.front(), context, arg); + return nullptr; } bool ComponentFactory::hasCreator(const std::string& classname) const diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 191721f2e9e..9c5c9d8388f 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -44,7 +44,7 @@ struct SOFA_CORE_API ComponentRegistrationData std::string componentName; std::set aliases; - std::unordered_map templateAttributes; + std::vector> templateAttributes; std::string componentNamespace; std::string componentModule; @@ -76,7 +76,7 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistra ComponentRegistrationDataBuilder& addTemplateAttribute( const std::string& templateAttribute, const std::string& value) { - this->templateAttributes[templateAttribute] = value; + this->templateAttributes.emplace_back(templateAttribute, value); return *this; } From af4ad095faeaf5b49dbd82593134e66a41dcf0dc Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 17:05:20 +0200 Subject: [PATCH 07/71] warning --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 7f2506273b3..439eb2d7b55 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -271,6 +271,8 @@ auto createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::Ba context->addObject(component); } + msg_warning_when(desc->componentModule.empty(), component.get()) << "Module name is empty"; + component->parse(arg); } From 1700e5c2b44c307c439be98d5f04ca8df9f91dfe Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 17:22:40 +0200 Subject: [PATCH 08/71] extend factory registration to RequiredPlugin and SparseLDLSolver --- .../linearsolver/direct/SparseLDLSolver.cpp | 19 ++++++++++++++++--- .../statecontainer/MechanicalObject.cpp | 1 - .../Core/src/sofa/core/ComponentFactory.cpp | 5 +++++ .../Core/src/sofa/core/ComponentFactory.h | 3 +++ .../src/sofa/simulation/RequiredPlugin.cpp | 8 ++++++-- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp index 5b336137077..a59f4765847 100644 --- a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp +++ b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp @@ -29,11 +29,24 @@ namespace sofa::component::linearsolver::direct using namespace sofa::linearalgebra; +constexpr std::string_view description { "Direct linear solver using a Sparse LDL^T factorization." }; + +template +void registerComponent(sofa::core::ComponentFactory* factory) +{ + factory->registerComponent>( + core::ComponentRegistrationDataBuilder() + .setName("SparseLDLSolver") + .addTemplateAttribute("matrixType") + .setDescription(std::string(description)) + .setModuleName(MODULE_NAME) + ); +}; + void registerSparseLDLSolver(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Direct linear solver using a Sparse LDL^T factorization.") - .add< SparseLDLSolver< CompressedRowSparseMatrix, FullVector > >(true) - .add< SparseLDLSolver< CompressedRowSparseMatrix >, FullVector > >()); + registerComponent, FullVector>(factory); + registerComponent>, FullVector>(factory); } template class SOFA_COMPONENT_LINEARSOLVER_DIRECT_API SparseLDLSolver< CompressedRowSparseMatrix,FullVector >; diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp index 1badda1bf77..c02d9d46cbe 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp @@ -49,7 +49,6 @@ void registerComponent(sofa::core::ComponentFactory* factory) factory->registerComponent>( core::ComponentRegistrationDataBuilder() .setName("MechanicalObject") - .addAlias("State") .addTemplateAttribute("dofType") .setDescription(std::string(description)) .setModuleName(MODULE_NAME) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 439eb2d7b55..c60cefa4a32 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -337,6 +337,11 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; } +objectmodel::BaseComponent::SPtr ComponentFactory::createObject( + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + return this->createComponent(context, arg); +} bool ComponentFactory::hasCreator(const std::string& classname) const { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index a10beb09410..a3728ca489d 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -65,6 +65,9 @@ class SOFA_CORE_API ComponentFactory /// Create a component given a context and a description. objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + // to deprecate + objectmodel::BaseComponent::SPtr createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + /// Test if a creator exists for a given classname diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp index a804cd092a0..d97372f6700 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp @@ -35,8 +35,12 @@ namespace sofa::simulation void registerRequiredPlugin(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Load the SOFA modules and/or plugins required to run a simulation.") - .add< RequiredPlugin >()); + factory->registerComponent( + sofa::core::ComponentRegistrationDataBuilder() + .setName("RequiredPlugin") + .setDescription("Load the SOFA modules and/or plugins required to run a simulation.") + .setModuleName("Sofa.Simulation.Core") + ); } RequiredPlugin::RequiredPlugin() From 9b6e08f4daac9708503de35f755b279e969bdd06 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 22:15:50 +0200 Subject: [PATCH 09/71] stream operator --- .../Core/src/sofa/core/ComponentFactory.cpp | 36 ++++++++++++------- .../src/sofa/core/ComponentRegistrationData.h | 16 +++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index c60cefa4a32..ad10a8140b1 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -310,29 +310,41 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - const auto matchingTemplates = selectCandidatesTemplateAttributes(candidates, arg); - - if (!matchingTemplates.empty()) + //exact template { - msg_warning_when(matchingTemplates.size() > 1) << "Multiple candidates with the same templates"; - return createComponentFrom(matchingTemplates.front(), context, arg); + const auto matchingTemplates = selectCandidatesTemplateAttributes(candidates, arg); + + if (!matchingTemplates.empty()) + { + msg_warning_when(matchingTemplates.size() > 1) << "Multiple candidates with the same templates: " << + sofa::helper::join(matchingTemplates.begin(), matchingTemplates.end(), ","); + return createComponentFrom(matchingTemplates.front(), context, arg); + } } // Selection based on the legacy 'template' keyword - const auto templateCandidates = selectCandidatesTemplateKeyword(candidates, arg); - if (!templateCandidates.empty()) { - return createComponentFrom(templateCandidates.front(), context, arg); + const auto templateCandidates = selectCandidatesTemplateKeyword(candidates, arg); + if (!templateCandidates.empty()) + { + msg_warning_when(templateCandidates.size() > 1) << "Multiple candidates with the same templates: " << + sofa::helper::join(templateCandidates.begin(), templateCandidates.end(), ","); + return createComponentFrom(templateCandidates.front(), context, arg); + } } //template deduction // There are candidates, but none of them match the templates // We select one of them automatically based on deduction rules - auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); - - if (!deducedCandidates.empty()) { - return createComponentFrom(deducedCandidates.front(), context, arg); + auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); + + if (!deducedCandidates.empty()) + { + msg_warning_when(deducedCandidates.size() > 1) << "Multiple candidates with the same templates: " << + sofa::helper::join(deducedCandidates.begin(), deducedCandidates.end(), ","); + return createComponentFrom(deducedCandidates.front(), context, arg); + } } return nullptr; diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 9c5c9d8388f..64591ba72b0 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -59,6 +59,22 @@ struct SOFA_CORE_API ComponentRegistrationData std::shared_ptr templateDeductionRule; }; +inline std::ostream& operator<<(std::ostream& os, const ComponentRegistrationData& data) +{ + os << data.componentName; + if (!data.templateAttributes.empty()) + { + os << "["; + os << sofa::helper::join(data.templateAttributes.begin(), data.templateAttributes.end(), + [](const std::pair& pair) + { + return pair.first + "=" + pair.second; + }, ','); + os << "]"; + } + return os; +} + struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistrationData { ComponentRegistrationDataBuilder& setName(const std::string& _componentName) From 0087f97e00e2e7a17da03398d4a06e5707dd5897 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 22:18:37 +0200 Subject: [PATCH 10/71] move to cpp --- .../Core/src/sofa/core/ComponentFactory.cpp | 15 ++++++++++++++ .../Core/src/sofa/core/ComponentFactory.h | 20 ------------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index ad10a8140b1..36ebeab2785 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -25,6 +25,21 @@ #include #include +namespace sofa::helper::logging +{ + +inline bool notMuted(const core::ComponentFactory*) +{ + return true; +} + +inline ComponentInfo::SPtr getComponentInfo(const core::ComponentFactory*) +{ + return std::make_shared("ComponentFactory"); +} + +} + namespace sofa::core { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index a3728ca489d..a6021d6e5cc 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -167,23 +167,3 @@ struct SOFA_CORE_API MainComponentFactory } - - - - - - - -namespace sofa::helper::logging -{ - -inline bool notMuted(const core::ComponentFactory*) -{ - return true; -} - -inline ComponentInfo::SPtr getComponentInfo(const core::ComponentFactory*) -{ - return std::make_shared("ComponentFactory"); -} -} From 079e09ea3cd906348a734cce8eec487a5c481e73 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 23 Jun 2026 22:23:24 +0200 Subject: [PATCH 11/71] don't expose --- .../Core/src/sofa/core/ComponentFactory.cpp | 82 ++++++++++--------- .../Core/src/sofa/core/ComponentFactory.h | 5 +- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 36ebeab2785..07e8a6145a6 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -43,44 +43,6 @@ inline ComponentInfo::SPtr getComponentInfo(const core::ComponentFactory*) namespace sofa::core { -std::vector ComponentFactory::getComponentsFromName(const std::string& componentName) const -{ - std::vector result; - - std::string componentToSearch = componentName; - - using sofa::helper::lifecycle::renamedComponents; - auto renamedComponent = renamedComponents.find(componentName); - if( renamedComponent != renamedComponents.end() ) - { - componentToSearch = renamedComponent->second.getNewName(); - } - - for (const auto& component : m_registry) - { - const auto fullName = component->componentModule + "." + component->componentName; - - if (component->componentName == componentToSearch || fullName == componentToSearch) - { - result.push_back(component); - } - else - { - for (const auto& alias : component->aliases) - { - const auto fullNameAlias = component->componentModule + "." + alias; - - if (alias == componentToSearch || fullNameAlias == componentToSearch) - { - result.push_back(component); - } - } - } - } - - return result; -} - typedef struct ObjectRegistrationEntry { inline static const char* symbol = "registerObjects"; @@ -174,6 +136,48 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) namespace { + + +std::vector getComponentsFromName( + const ComponentFactory& self, + const std::string& componentName) +{ + std::vector result; + + std::string componentToSearch = componentName; + + using sofa::helper::lifecycle::renamedComponents; + auto renamedComponent = renamedComponents.find(componentName); + if( renamedComponent != renamedComponents.end() ) + { + componentToSearch = renamedComponent->second.getNewName(); + } + + for (const auto& component : self.getRegistry()) + { + const auto fullName = component->componentModule + "." + component->componentName; + + if (component->componentName == componentToSearch || fullName == componentToSearch) + { + result.push_back(component); + } + else + { + for (const auto& alias : component->aliases) + { + const auto fullNameAlias = component->componentModule + "." + alias; + + if (alias == componentToSearch || fullNameAlias == componentToSearch) + { + result.push_back(component); + } + } + } + } + + return result; +} + void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& classname) { // The last dot separates the module name from the component name @@ -310,7 +314,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::string classname {typeAttribute}; autoLoadPluginIfNameContainsPluginName(*this, classname); - auto candidates = this->getComponentsFromName(classname); + auto candidates = getComponentsFromName(*this, classname); if (candidates.empty()) { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index a6021d6e5cc..4498f469c2f 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -46,7 +46,8 @@ class SOFA_CORE_API ComponentFactory // to deprecate using ClassEntry = ComponentDescription; - std::vector getComponentsFromName(const std::string& componentName) const; + using Registry = std::vector; + const Registry& getRegistry() const { return m_registry; } bool registerObjectsFromPlugin(const std::string& pluginName); @@ -101,7 +102,7 @@ class SOFA_CORE_API ComponentFactory using RegisteredPluginSet = std::set; RegisteredPluginSet m_registeredPluginSet; - std::vector m_registry; + Registry m_registry; }; From 34a7d4469555e3cb536f710c714417bf47436072 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 25 Jun 2026 09:21:39 +0200 Subject: [PATCH 12/71] support legacy registration --- .../Core/src/sofa/core/ComponentFactory.cpp | 87 ++++++++++--------- .../Core/src/sofa/core/ComponentFactory.h | 1 + .../src/sofa/core/ComponentRegistrationData.h | 44 +++++++++- 3 files changed, 85 insertions(+), 47 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 07e8a6145a6..0b33768d618 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -89,46 +89,47 @@ bool ComponentFactory::registerObjectsFromPlugin(const std::string& pluginName) bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) { - // auto& creators = ro.creators; - // - // if (creators.empty()) - // { - // msg_error() << "No creator provided"; - // return false; - // } - // - // for (auto& creator : ro.creators) - // { - // ComponentDescription::SPtr component = std::make_shared(); - // - // component->componentName = ro.componentName; - // component->aliases = ro.aliases; - // component->componentNamespace = ro.componentNamespace; - // component->componentModule = ro.componentModule; - // - // component->description = ro.description; - // component->authors = sofa::helper::join(ro.authors, ","); - // component->license = ro.license; - // component->documentationURL = ro.documentationURL; - // - // { - // //special cases for official documentation - // const auto modulePaths = sofa::helper::split(component->componentModule, '.'); - // if (modulePaths.size() > 2 && modulePaths[0] == "Sofa" && modulePaths[1] == "Component") - // { - // std::string officialDocURL = std::string(sofa::SOFA_DOCUMENTATION_URL) + std::string("components/"); - // officialDocURL += sofa::helper::join(modulePaths.begin() + 2, modulePaths.end(), - // [](const std::string& m){ return sofa::helper::downcaseString(m);}, "/"); - // officialDocURL += std::string("/") + sofa::helper::downcaseString(component->componentName); - // - // component->documentationURL.insert(officialDocURL); - // } - // } - // - // component->creator = std::move(creator); - // - // this->m_registry.push_back(component); - // } + auto& creators = ro.m_componentCreators; + + if (creators.empty()) + { + msg_error() << "No creator provided"; + return false; + } + + for (std::size_t i = 0; i < creators.size(); ++i) + { + auto creator = creators[i]; + ComponentDescription::SPtr component = std::make_shared(); + + component->componentName = ro.m_componentNames[i]; + component->aliases = ro.m_aliases; + component->componentNamespace = ""; + component->componentModule = ""; + + component->description = ro.m_description; + component->authors.insert(ro.m_authors); + component->license = ro.m_license; + component->documentationURL.insert(ro.m_documentationURL); + + { + //special cases for official documentation + const auto modulePaths = sofa::helper::split(component->componentModule, '.'); + if (modulePaths.size() > 2 && modulePaths[0] == "Sofa" && modulePaths[1] == "Component") + { + std::string officialDocURL = std::string(sofa::SOFA_DOCUMENTATION_URL) + std::string("components/"); + officialDocURL += sofa::helper::join(modulePaths.begin() + 2, modulePaths.end(), + [](const std::string& m){ return sofa::helper::downcaseString(m);}, "/"); + officialDocURL += std::string("/") + sofa::helper::downcaseString(component->componentName); + + component->documentationURL.insert(officialDocURL); + } + } + + component->creator = creator->clone(); + + this->m_registry.push_back(component); + } return true; @@ -329,7 +330,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - //exact template + //exact template: could it be a template deduction rule? { const auto matchingTemplates = selectCandidatesTemplateAttributes(candidates, arg); @@ -352,8 +353,8 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } - //template deduction - // There are candidates, but none of them match the templates + // Template deduction: + // So far, none of the candidates match the templates. // We select one of them automatically based on deduction rules { auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 4498f469c2f..8d7ba0926ec 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -33,6 +33,7 @@ struct SOFA_CORE_API ComponentDescription { using SPtr = std::shared_ptr; + ComponentDescription() = default; explicit ComponentDescription(const ComponentRegistrationData& data) : ComponentRegistrationData(data) {} diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 64591ba72b0..23942916958 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -136,25 +136,61 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistra //to deprecate struct SOFA_CORE_API LegacyComponentRegistrationData { - explicit LegacyComponentRegistrationData(const std::string&) {} + std::string m_description; + explicit LegacyComponentRegistrationData(const std::string& description) + : m_description(description) + {} + + std::vector m_componentCreators; + ~LegacyComponentRegistrationData() + { + for (auto* componentCreator : m_componentCreators) + { + delete componentCreator; + } + } + + std::vector m_componentNames; template LegacyComponentRegistrationData& add(bool = false) { + m_componentCreators.push_back(new ComponentCreator); + m_componentNames.push_back(T::GetClass()->className); + //to do: deal with templates + return *this; + } + + std::string m_documentationURL; + LegacyComponentRegistrationData& addDocumentationURL(const std::string& documentationURL) + { + m_documentationURL = documentationURL; + return *this; + } + + LegacyComponentRegistrationData& addDescription(const std::string& description) + { + m_description = description; return *this; } - LegacyComponentRegistrationData& addDocumentationURL(const std::string&) + std::set m_aliases; + LegacyComponentRegistrationData& addAlias(const std::string& alias) { + m_aliases.insert(alias); return *this; } - LegacyComponentRegistrationData& addDescription(const std::string&) + std::string m_authors; + LegacyComponentRegistrationData& addAuthor(const std::string& author) { + m_authors = author; return *this; } - LegacyComponentRegistrationData& addAlias(const std::string&) + std::string m_license; + LegacyComponentRegistrationData& addLicense(const std::string& license) { + m_license = license; return *this; } }; From ea0b6705b75fae23630b20514aea7e3491a1e8b4 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 25 Jun 2026 09:21:58 +0200 Subject: [PATCH 13/71] convert CorotationalFEMForceField --- .../fem/elastic/CorotationalFEMForceField.cpp | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index efd7c5d6bca..fdbf5f1878f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -29,22 +29,35 @@ namespace sofa::component::solidmechanics::fem::elastic { -void registerCorotationalFEMForceField(sofa::core::ObjectFactory* factory) +static constexpr std::string_view description { "Hooke's law using the corotational approach" }; + +template +void registerComponent(sofa::core::ObjectFactory* factory) { - factory->registerObjects(sofa::core::ObjectRegistrationData("Hooke's law using the corotational approach") - // .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() - .add< CorotationalFEMForceField >() + factory->registerComponent>( + core::ComponentRegistrationDataBuilder() + .setName("CorotationalFEMForceField") + .addTemplateAttribute("dofType") + .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) + .setDescription(std::string(description)) + .setModuleName(MODULE_NAME) ); } +void registerCorotationalFEMForceField(sofa::core::ObjectFactory* factory) +{ + // registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); +} + // template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API CorotationalFEMForceField; template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API CorotationalFEMForceField; template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API CorotationalFEMForceField; From f7c8d8599504522eb383e37a9ec5fa249c51c990 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 25 Jun 2026 11:19:53 +0200 Subject: [PATCH 14/71] partial template matching --- .../Core/src/sofa/core/ComponentFactory.cpp | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 0b33768d618..f11de32ea7a 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -201,9 +201,9 @@ void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::s } } -std::vector selectCandidatesTemplateAttributes(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) +std::vector selectCandidatesFromTemplateAttributes(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) { - std::vector matchingCandidates; + std::vector exactlyMatchingCandidates; for (const auto& candidate : candidates) { @@ -227,11 +227,36 @@ std::vector selectCandidatesTemplateAttributes(const if (matchAllTemplateParameters) { - matchingCandidates.push_back(candidate); + exactlyMatchingCandidates.push_back(candidate); } + } - return matchingCandidates; + return exactlyMatchingCandidates; +} + +std::vector selectCandidatesFromPartialTemplateAttributes(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) +{ + std::vector partiallyMatchingCandidates; + + for (const auto& candidate : candidates) + { + for (const auto& [attribute, value] : candidate->templateAttributes) + { + const char* attr = arg->getAttribute(attribute, nullptr); + if (attr != nullptr) + { + const std::string attrStr { attr }; + if (defaulttype::TemplateAliases::resolveAlias(attrStr) == value) + { + partiallyMatchingCandidates.push_back(candidate); + break; + } + } + } + } + + return partiallyMatchingCandidates; } std::vector selectCandidatesTemplateKeyword( @@ -332,7 +357,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( //exact template: could it be a template deduction rule? { - const auto matchingTemplates = selectCandidatesTemplateAttributes(candidates, arg); + const auto matchingTemplates = selectCandidatesFromTemplateAttributes(candidates, arg); if (!matchingTemplates.empty()) { @@ -353,6 +378,24 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } + //partial template matching + { + const auto matchingTemplates = selectCandidatesFromPartialTemplateAttributes(candidates, arg); + + if (matchingTemplates.size() == 1) + { + return createComponentFrom(matchingTemplates.front(), context, arg); + } + + auto deducedCandidates = selectCandidatesDeductionRules(matchingTemplates, context, arg); + if (!deducedCandidates.empty()) + { + msg_warning_when(deducedCandidates.size() > 1) << "Multiple candidates with the same templates: " << + sofa::helper::join(deducedCandidates.begin(), deducedCandidates.end(), ","); + return createComponentFrom(deducedCandidates.front(), context, arg); + } + } + // Template deduction: // So far, none of the candidates match the templates. // We select one of them automatically based on deduction rules @@ -367,6 +410,8 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } + msg_error() << "Cannot create component " << classname; + return nullptr; } objectmodel::BaseComponent::SPtr ComponentFactory::createObject( From 4a78b69d0fbd226c5ab9a390037ed21564bc2192 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 11:05:53 +0200 Subject: [PATCH 15/71] in progress --- .../Core/src/sofa/core/ComponentFactory.cpp | 74 +++++++++++++++---- .../src/sofa/core/ComponentRegistrationData.h | 2 +- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index f11de32ea7a..59f857ef076 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace sofa::helper::logging @@ -179,16 +180,31 @@ std::vector getComponentsFromName( return result; } -void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& classname) +void extractModuleName(const std::string& inputClassName, std::string& className, std::string& moduleName) { // The last dot separates the module name from the component name // Example: Module.Name.ComponentName (it is common to have dots in the module names) // It is assumed that the component name does not contain any dot - auto lastDot = classname.find_last_of('.'); + auto lastDot = inputClassName.find_last_of('.'); if (lastDot != std::string::npos) { - const auto pluginName = classname.substr(0, lastDot); + moduleName = inputClassName.substr(0, lastDot); + className = inputClassName.substr(lastDot + 1); + } + else + { + moduleName = {}; + className = inputClassName; + } +} + +void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& inputClassName) +{ + std::string classname, pluginName; + extractModuleName(inputClassName, classname, pluginName); + if (!pluginName.empty()) + { const auto [path, loaded] = helper::system::PluginManager::getInstance().isPluginLoaded(pluginName); if (!loaded) { @@ -295,8 +311,9 @@ std::vector selectCandidatesDeductionRules( for (const auto& candidate : candidates) { + // Check if the component has a rule AND if that rule applies. if (const auto rule = candidate->templateDeductionRule; - rule && rule->doesTemplateDeductionApply(context, arg)) + rule && rule->doesComponentComplyWith(context, arg)) { matchingCandidates.push_back(candidate); } @@ -324,6 +341,18 @@ auto createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::Ba return component; } +std::vector similarComponentNames(const ComponentFactory& self, const std::string& className) +{ + std::set allClassNames; + std::transform(self.getRegistry().begin(), self.getRegistry().end(), std::inserter(allClassNames, allClassNames.begin()), + [](const ComponentDescription::SPtr& component){ return component->componentName; }); + const auto result = sofa::helper::getClosestMatch(className, std::vector(allClassNames.begin(), allClassNames.end())); + std::vector similarComponentNames; + std::transform(result.begin(), result.end(), std::back_inserter(similarComponentNames), + [](const auto& match) { return std::get<0>(match); } ); + return similarComponentNames; +} + } objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( @@ -337,13 +366,26 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( if (typeAttribute == nullptr) return nullptr; - std::string classname {typeAttribute}; + std::string inputClassName {typeAttribute}; + std::string classname, pluginName; + extractModuleName(inputClassName, classname, pluginName); + autoLoadPluginIfNameContainsPluginName(*this, classname); - auto candidates = getComponentsFromName(*this, classname); + std::vector candidates = getComponentsFromName(*this, classname); if (candidates.empty()) { + std::stringstream ss; + ss << "Cannot create component '" << classname << "': '" << classname << "' not found in the factory."; + + const auto similarNames = similarComponentNames(*this, classname); + if (!similarNames.empty()) + { + ss << " Some components were font with similar names: " << sofa::helper::join(similarNames, ", "); + } + + msg_error() << ss.str(); return nullptr; } @@ -382,22 +424,26 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( { const auto matchingTemplates = selectCandidatesFromPartialTemplateAttributes(candidates, arg); + // only one candidate matches partially: this is the one if (matchingTemplates.size() == 1) { return createComponentFrom(matchingTemplates.front(), context, arg); } - - auto deducedCandidates = selectCandidatesDeductionRules(matchingTemplates, context, arg); - if (!deducedCandidates.empty()) + // otherwise, we discriminate them using deduction rules + else if (!matchingTemplates.empty()) { - msg_warning_when(deducedCandidates.size() > 1) << "Multiple candidates with the same templates: " << - sofa::helper::join(deducedCandidates.begin(), deducedCandidates.end(), ","); - return createComponentFrom(deducedCandidates.front(), context, arg); + auto deducedCandidates = selectCandidatesDeductionRules(matchingTemplates, context, arg); + if (!deducedCandidates.empty()) + { + msg_warning_when(deducedCandidates.size() > 1) << "Multiple candidates with the same templates: " << + sofa::helper::join(deducedCandidates.begin(), deducedCandidates.end(), ","); + return createComponentFrom(deducedCandidates.front(), context, arg); + } } } // Template deduction: - // So far, none of the candidates match the templates. + // So far, none of the candidates match the template attributes. // We select one of them automatically based on deduction rules { auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); @@ -410,7 +456,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } - msg_error() << "Cannot create component " << classname; + msg_error() << "Cannot create component '" << classname << "'"; return nullptr; } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 23942916958..b5b13d9f2f5 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -34,7 +34,7 @@ namespace sofa::core struct SOFA_CORE_API BaseTemplateDeductionRule { - virtual bool doesTemplateDeductionApply( + virtual bool doesComponentComplyWith( objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) = 0; }; From a550a5387f97f7b5f28db209504f68bfb8456cf7 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 11:15:10 +0200 Subject: [PATCH 16/71] clearer --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 59f857ef076..e34b3892ca1 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -198,11 +198,8 @@ void extractModuleName(const std::string& inputClassName, std::string& className } } -void autoLoadPluginIfNameContainsPluginName(ComponentFactory& self, const std::string& inputClassName) +void autoLoadPlugin(ComponentFactory& self, const std::string& pluginName) { - std::string classname, pluginName; - extractModuleName(inputClassName, classname, pluginName); - if (!pluginName.empty()) { const auto [path, loaded] = helper::system::PluginManager::getInstance().isPluginLoaded(pluginName); @@ -370,7 +367,10 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::string classname, pluginName; extractModuleName(inputClassName, classname, pluginName); - autoLoadPluginIfNameContainsPluginName(*this, classname); + if (!pluginName.empty()) + { + autoLoadPlugin(*this, pluginName); + } std::vector candidates = getComponentsFromName(*this, classname); From adf3da555482b6f5072d9121b0ff5bcd57739272 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 11:26:35 +0200 Subject: [PATCH 17/71] filter if plugins don't match --- .../Core/src/sofa/core/ComponentFactory.cpp | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index e34b3892ca1..1b88ee3dbb7 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -142,7 +142,8 @@ namespace std::vector getComponentsFromName( const ComponentFactory& self, - const std::string& componentName) + const std::string& componentName, + const std::string& pluginName) { std::vector result; @@ -157,6 +158,11 @@ std::vector getComponentsFromName( for (const auto& component : self.getRegistry()) { + if (!pluginName.empty() && component->componentModule != pluginName) + { + continue; + } + const auto fullName = component->componentModule + "." + component->componentName; if (component->componentName == componentToSearch || fullName == componentToSearch) @@ -343,7 +349,7 @@ std::vector similarComponentNames(const ComponentFactory& self, con std::set allClassNames; std::transform(self.getRegistry().begin(), self.getRegistry().end(), std::inserter(allClassNames, allClassNames.begin()), [](const ComponentDescription::SPtr& component){ return component->componentName; }); - const auto result = sofa::helper::getClosestMatch(className, std::vector(allClassNames.begin(), allClassNames.end())); + const auto result = sofa::helper::getClosestMatch(className, std::vector(allClassNames.begin(), allClassNames.end()), 5, 0.6); std::vector similarComponentNames; std::transform(result.begin(), result.end(), std::back_inserter(similarComponentNames), [](const auto& match) { return std::get<0>(match); } ); @@ -364,22 +370,22 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; std::string inputClassName {typeAttribute}; - std::string classname, pluginName; - extractModuleName(inputClassName, classname, pluginName); + std::string className, pluginName; + extractModuleName(inputClassName, className, pluginName); if (!pluginName.empty()) { autoLoadPlugin(*this, pluginName); } - std::vector candidates = getComponentsFromName(*this, classname); + std::vector candidates = getComponentsFromName(*this, className, pluginName); if (candidates.empty()) { std::stringstream ss; - ss << "Cannot create component '" << classname << "': '" << classname << "' not found in the factory."; + ss << "Cannot create component '" << className << "': '" << className << "' not found in the factory."; - const auto similarNames = similarComponentNames(*this, classname); + const auto similarNames = similarComponentNames(*this, className); if (!similarNames.empty()) { ss << " Some components were font with similar names: " << sofa::helper::join(similarNames, ", "); @@ -456,7 +462,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } - msg_error() << "Cannot create component '" << classname << "'"; + msg_error() << "Cannot create component '" << className << "'"; return nullptr; } From 4fcea7a4ccd94e5ddc6c120e9a2ea40ebe34c343 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 11:31:41 +0200 Subject: [PATCH 18/71] typo --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 1b88ee3dbb7..ac7dc2c85a7 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -388,7 +388,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( const auto similarNames = similarComponentNames(*this, className); if (!similarNames.empty()) { - ss << " Some components were font with similar names: " << sofa::helper::join(similarNames, ", "); + ss << " Some components were found with similar names: " << sofa::helper::join(similarNames, ", "); } msg_error() << ss.str(); From 1b7a49269ed505a32a6130e2241fde812762f11c Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 14:17:57 +0200 Subject: [PATCH 19/71] message unloaded plugins --- .../Core/src/sofa/core/ComponentFactory.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index ac7dc2c85a7..7742fd95be7 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -395,10 +395,24 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; } - std::erase_if(candidates, [](const ComponentDescription::SPtr& candidate) { - return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); - }); + auto candidatesWithoutUnloadedPlugins = candidates; + std::erase_if(candidatesWithoutUnloadedPlugins, [](const ComponentDescription::SPtr& candidate) + { + return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); + }); + + if (candidatesWithoutUnloadedPlugins.empty()) + { + std::set unloadedPlugins; + std::transform(candidates.begin(), candidates.end(), std::inserter(unloadedPlugins, unloadedPlugins.begin()), + [](const ComponentDescription::SPtr& component) { return component->componentModule; }); + const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end()); + msg_error() << "Trying to create component '" << className + << "' but candidates have been found in unloaded plugins:" << unloadedPluginsString << "]"; + return nullptr; + } + } std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); From 06b1910a54e8c18364f4e80df926f5eb22455d9f Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 14:57:40 +0200 Subject: [PATCH 20/71] known issues --- .../Core/src/sofa/core/ComponentFactory.cpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 7742fd95be7..8c77557b974 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -356,6 +356,28 @@ std::vector similarComponentNames(const ComponentFactory& self, con return similarComponentNames; } +bool knownIssues(const ComponentFactory& self, const std::string& clasName) +{ + auto uncreatableComponent = helper::lifecycle::uncreatableComponents.find(clasName); + auto dealiasedComponent = helper::lifecycle::dealiasedComponents.find(clasName); + + const bool isUncreatable = uncreatableComponent != helper::lifecycle::uncreatableComponents.end(); + const bool isDealiased = dealiasedComponent != helper::lifecycle::dealiasedComponents.end(); + + if (isUncreatable) + { + msg_error(&self) << uncreatableComponent->second.getMessage(); + return true; + } + if (isDealiased) + { + msg_error(&self) << dealiasedComponent->second.getMessage(); + return true; + } + + return false; +} + } objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( @@ -373,6 +395,11 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::string className, pluginName; extractModuleName(inputClassName, className, pluginName); + if (knownIssues(*this, className)) + { + return nullptr; + } + if (!pluginName.empty()) { autoLoadPlugin(*this, pluginName); From 8892ad749ece92f1ee63292bad3e0b72bd04cb3d Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 15:14:11 +0200 Subject: [PATCH 21/71] cleaning --- .../Core/src/sofa/core/ComponentFactory.cpp | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 8c77557b974..e0e98e0f419 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -391,37 +391,44 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( if (typeAttribute == nullptr) return nullptr; - std::string inputClassName {typeAttribute}; - std::string className, pluginName; - extractModuleName(inputClassName, className, pluginName); + std::string componentName, moduleName; + extractModuleName(std::string{typeAttribute}, componentName, moduleName); - if (knownIssues(*this, className)) + if (knownIssues(*this, componentName)) { return nullptr; } - if (!pluginName.empty()) + if (auto it = helper::lifecycle::movedComponents.find(componentName); + it != helper::lifecycle::movedComponents.end()) { - autoLoadPlugin(*this, pluginName); + // autoLoadPlugin(*this, ); } - std::vector candidates = getComponentsFromName(*this, className, pluginName); + if (!moduleName.empty()) + { + autoLoadPlugin(*this, moduleName); + } + std::vector candidates = getComponentsFromName(*this, componentName, moduleName); + + // Early failure because there are no compatible candidates if (candidates.empty()) { std::stringstream ss; - ss << "Cannot create component '" << className << "': '" << className << "' not found in the factory."; + ss << "Cannot create component '" << componentName << "': '" << componentName << "' not found in the factory registry."; - const auto similarNames = similarComponentNames(*this, className); + const auto similarNames = similarComponentNames(*this, componentName); if (!similarNames.empty()) { - ss << " Some components were found with similar names: " << sofa::helper::join(similarNames, ", "); + ss << " Suggestion: Components were found with similar names: " << sofa::helper::join(similarNames, ", "); } msg_error() << ss.str(); return nullptr; } + // Check that the candidates don't rely on unloaded modules { auto candidatesWithoutUnloadedPlugins = candidates; std::erase_if(candidatesWithoutUnloadedPlugins, [](const ComponentDescription::SPtr& candidate) @@ -435,8 +442,8 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::transform(candidates.begin(), candidates.end(), std::inserter(unloadedPlugins, unloadedPlugins.begin()), [](const ComponentDescription::SPtr& component) { return component->componentModule; }); const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end()); - msg_error() << "Trying to create component '" << className - << "' but candidates have been found in unloaded plugins:" << unloadedPluginsString << "]"; + msg_error() << "Attempted to create component '" << componentName + << "' but all potential candidates rely on component from currently unloaded plugins:" << unloadedPluginsString << "]"; return nullptr; } } @@ -444,19 +451,19 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - //exact template: could it be a template deduction rule? + // 1. Attempt Exact Template Match (Highest Priority). { - const auto matchingTemplates = selectCandidatesFromTemplateAttributes(candidates, arg); + const auto exactMatches = selectCandidatesFromTemplateAttributes(candidates, arg); - if (!matchingTemplates.empty()) + if (!exactMatches.empty()) { - msg_warning_when(matchingTemplates.size() > 1) << "Multiple candidates with the same templates: " << - sofa::helper::join(matchingTemplates.begin(), matchingTemplates.end(), ","); - return createComponentFrom(matchingTemplates.front(), context, arg); + msg_warning_when(exactMatches.size() > 1) << "Multiple candidates with the same templates: " << + sofa::helper::join(exactMatches.begin(), exactMatches.end(), ","); + return createComponentFrom(exactMatches.front(), context, arg); } } - // Selection based on the legacy 'template' keyword + // 2. Attempt Selection by Legacy 'template' Keyword (Medium-High Priority). { const auto templateCandidates = selectCandidatesTemplateKeyword(candidates, arg); if (!templateCandidates.empty()) @@ -467,18 +474,18 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } - //partial template matching + // 3. Attempt Partial Template Matching with Deduction (Medium Priority). { const auto matchingTemplates = selectCandidatesFromPartialTemplateAttributes(candidates, arg); - // only one candidate matches partially: this is the one if (matchingTemplates.size() == 1) { + // Success: Only one candidate matches partially -> Select it. return createComponentFrom(matchingTemplates.front(), context, arg); } - // otherwise, we discriminate them using deduction rules else if (!matchingTemplates.empty()) { + // Ambiguous partial match: Use deduction rules to resolve ambiguity. auto deducedCandidates = selectCandidatesDeductionRules(matchingTemplates, context, arg); if (!deducedCandidates.empty()) { @@ -489,9 +496,8 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } - // Template deduction: - // So far, none of the candidates match the template attributes. - // We select one of them automatically based on deduction rules + // 4. Attempt General Template Deduction (Lowest Priority). + // If no explicit template matching worked, try to select based purely on deduction rules from all valid candidates. { auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); @@ -503,7 +509,16 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } - msg_error() << "Cannot create component '" << className << "'"; + // 5. Final fallback + { + if (!candidates.empty()) + { + return createComponentFrom(candidates.front(), context, arg); + } + } + + // Final failure + msg_error() << "Could not find or select a unique component for '" << componentName << "'"; return nullptr; } From 08248c8c493e952c67315c9127137ac291e2132e Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 15:46:07 +0200 Subject: [PATCH 22/71] more deduction rules --- .../fem/elastic/CorotationalFEMForceField.cpp | 1 + Sofa/framework/Core/CMakeLists.txt | 1 + .../Core/src/sofa/core/ComponentFactory.cpp | 2 +- .../src/sofa/core/ComponentRegistrationData.h | 15 +++-- .../src/sofa/core/TemplateDeductionRules.h | 66 +++++++++++++++++++ 5 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index fdbf5f1878f..deca7f643c7 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -41,6 +41,7 @@ void registerComponent(sofa::core::ObjectFactory* factory) .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) .setDescription(std::string(description)) .setModuleName(MODULE_NAME) + .setDeductionRule>() ); } diff --git a/Sofa/framework/Core/CMakeLists.txt b/Sofa/framework/Core/CMakeLists.txt index 33881334318..0df4d035b3a 100644 --- a/Sofa/framework/Core/CMakeLists.txt +++ b/Sofa/framework/Core/CMakeLists.txt @@ -67,6 +67,7 @@ set(HEADER_FILES ${SRC_ROOT}/sptr.h ${SRC_ROOT}/State.h ${SRC_ROOT}/State.inl + ${SRC_ROOT}/TemplateDeductionRules.h ${SRC_ROOT}/VecId.h ${SRC_ROOT}/behavior/BaseAnimationLoop.h ${SRC_ROOT}/behavior/BaseConstraint.h diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index e0e98e0f419..127cb66e0db 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -441,7 +441,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::set unloadedPlugins; std::transform(candidates.begin(), candidates.end(), std::inserter(unloadedPlugins, unloadedPlugins.begin()), [](const ComponentDescription::SPtr& component) { return component->componentModule; }); - const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end()); + const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end(), ", "); msg_error() << "Attempted to create component '" << componentName << "' but all potential candidates rely on component from currently unloaded plugins:" << unloadedPluginsString << "]"; return nullptr; diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index b5b13d9f2f5..f698e8b6492 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -32,13 +33,6 @@ namespace sofa::core { -struct SOFA_CORE_API BaseTemplateDeductionRule -{ - virtual bool doesComponentComplyWith( - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) = 0; -}; - struct SOFA_CORE_API ComponentRegistrationData { std::string componentName; @@ -131,6 +125,13 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistra this->documentationURL.insert(_documentationURL); return *this; } + + template + ComponentRegistrationDataBuilder& setDeductionRule() + { + this->templateDeductionRule = std::make_shared(); + return *this; + } }; //to deprecate diff --git a/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h b/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h new file mode 100644 index 00000000000..88bdf06be76 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h @@ -0,0 +1,66 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include + +#include + +namespace sofa::core +{ + +struct SOFA_CORE_API BaseTemplateDeductionRule +{ + bool doesComponentComplyWith( + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) + { + if (!context) return false; + return doDoesComponentComplyWith(context, arg); + }; + +protected: + + virtual bool doDoesComponentComplyWith( + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) = 0; +}; + +template +struct OtherComponentsInContextDeductionRule : public BaseTemplateDeductionRule +{ +protected: + bool doDoesComponentComplyWith( + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) override + { + SOFA_UNUSED(arg); + return ((context->get() != nullptr) && ...); + } +}; + +template +using MechanicalStateDeductionRule = OtherComponentsInContextDeductionRule>; + + + +} From 133fe2cf4f5e0c2180beb36cef7b0e43c58bda04 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 26 Jun 2026 15:59:19 +0200 Subject: [PATCH 23/71] more compatibility --- .../Core/src/sofa/core/ComponentFactory.cpp | 1 + .../Core/src/sofa/core/ComponentRegistrationData.h | 2 ++ .../Core/src/sofa/core/TemplateDeductionRules.h | 12 ++++++++++++ 3 files changed, 15 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 127cb66e0db..4e456aa6da3 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -128,6 +128,7 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) } component->creator = creator->clone(); + component->templateDeductionRule = ro.m_templateDeductionRules[i]; this->m_registry.push_back(component); } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index f698e8b6492..2c1079969a7 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -153,10 +153,12 @@ struct SOFA_CORE_API LegacyComponentRegistrationData } std::vector m_componentNames; + std::vector > m_templateDeductionRules; template LegacyComponentRegistrationData& add(bool = false) { m_componentCreators.push_back(new ComponentCreator); m_componentNames.push_back(T::GetClass()->className); + m_templateDeductionRules.push_back(std::make_shared>()); //to do: deal with templates return *this; } diff --git a/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h b/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h index 88bdf06be76..1641c322273 100644 --- a/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h +++ b/Sofa/framework/Core/src/sofa/core/TemplateDeductionRules.h @@ -61,6 +61,18 @@ struct OtherComponentsInContextDeductionRule : public BaseTemplateDeductionRule template using MechanicalStateDeductionRule = OtherComponentsInContextDeductionRule>; +template +struct CanCreateDeductionRule : public BaseTemplateDeductionRule +{ +protected: + bool doDoesComponentComplyWith( + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) override + { + T* instance = nullptr; + return T::canCreate(instance, context, arg); + } +}; } From 15eec5a6d43b55099fc5b67d0c0068ed1f215aa3 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sat, 27 Jun 2026 06:19:16 +0200 Subject: [PATCH 24/71] factorization --- .../Core/src/sofa/core/ComponentFactory.cpp | 132 +++++++++++------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 4e456aa6da3..01004e6d04f 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -221,8 +221,13 @@ void autoLoadPlugin(ComponentFactory& self, const std::string& pluginName) } } -std::vector selectCandidatesFromTemplateAttributes(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) +std::vector selectCandidatesFromTemplateAttributes( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) { + SOFA_UNUSED(context); + std::vector exactlyMatchingCandidates; for (const auto& candidate : candidates) @@ -255,8 +260,13 @@ std::vector selectCandidatesFromTemplateAttributes(c return exactlyMatchingCandidates; } -std::vector selectCandidatesFromPartialTemplateAttributes(const std::vector& candidates, objectmodel::BaseObjectDescription* arg) +std::vector selectCandidatesFromPartialTemplateAttributes( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) { + SOFA_UNUSED(context); + std::vector partiallyMatchingCandidates; for (const auto& candidate : candidates) @@ -281,8 +291,11 @@ std::vector selectCandidatesFromPartialTemplateAttri std::vector selectCandidatesTemplateKeyword( const std::vector& candidates, + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { + SOFA_UNUSED(context); + const char* templateAttr = arg->getAttribute("template", nullptr); if (!templateAttr) return {}; @@ -306,6 +319,16 @@ std::vector selectCandidatesTemplateKeyword( return matchingCandidates; } +std::vector noFilter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) +{ + SOFA_UNUSED(context); + SOFA_UNUSED(arg); + return candidates; +} + std::vector selectCandidatesDeductionRules( const std::vector& candidates, objectmodel::BaseContext* context, @@ -326,7 +349,7 @@ std::vector selectCandidatesDeductionRules( return matchingCandidates; } -auto createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +objectmodel::BaseComponent::SPtr createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { auto component = desc->creator->create(); @@ -379,6 +402,44 @@ bool knownIssues(const ComponentFactory& self, const std::string& clasName) return false; } +using Filter = std::vector (*)(const std::vector&, objectmodel::BaseContext*, objectmodel::BaseObjectDescription*); + +objectmodel::BaseComponent::SPtr applyFilter( + const ComponentFactory& self, + const std::string& componentName, + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg, + Filter filter) +{ + const auto filteredCandidates = filter(candidates, context, arg); + + if (!filteredCandidates.empty()) + { + if (filteredCandidates.size() == 1) + { + // No ambiguity: The unique candidate is returned + return createComponentFrom(filteredCandidates.front(), context, arg); + } + else + { + // Multiple candidates: Use deduction rules to resolve ambiguity. + auto deducedCandidates = selectCandidatesDeductionRules(filteredCandidates, context, arg); + if (!deducedCandidates.empty()) + { + msg_warning_when(deducedCandidates.size() > 1, &self) + << "Attempt to create component '" << componentName + << "', however multiple potential candidates match the provided attributes (" + << sofa::helper::join( + deducedCandidates.begin(), deducedCandidates.end(), ", ") + << "). The first one is selected."; + return createComponentFrom(deducedCandidates.front(), context, arg); + } + } + } + return nullptr; +} + } objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( @@ -449,73 +510,44 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( } } + // In case of ambiguity (multiple candidates), sorting candidates will allow returning the + // component with the highest priority. std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); // 1. Attempt Exact Template Match (Highest Priority). + if (auto component = applyFilter(*this, componentName, candidates, context, arg, + selectCandidatesFromTemplateAttributes)) { - const auto exactMatches = selectCandidatesFromTemplateAttributes(candidates, arg); - - if (!exactMatches.empty()) - { - msg_warning_when(exactMatches.size() > 1) << "Multiple candidates with the same templates: " << - sofa::helper::join(exactMatches.begin(), exactMatches.end(), ","); - return createComponentFrom(exactMatches.front(), context, arg); - } + return component; } // 2. Attempt Selection by Legacy 'template' Keyword (Medium-High Priority). + if (auto component = applyFilter(*this, componentName, candidates, context, arg, + selectCandidatesTemplateKeyword)) { - const auto templateCandidates = selectCandidatesTemplateKeyword(candidates, arg); - if (!templateCandidates.empty()) - { - msg_warning_when(templateCandidates.size() > 1) << "Multiple candidates with the same templates: " << - sofa::helper::join(templateCandidates.begin(), templateCandidates.end(), ","); - return createComponentFrom(templateCandidates.front(), context, arg); - } + return component; } - // 3. Attempt Partial Template Matching with Deduction (Medium Priority). + // 3. Attempt Partial Template Matching (Medium Priority). + if (auto component = applyFilter(*this, componentName, candidates, context, arg, + selectCandidatesFromPartialTemplateAttributes)) { - const auto matchingTemplates = selectCandidatesFromPartialTemplateAttributes(candidates, arg); - - if (matchingTemplates.size() == 1) - { - // Success: Only one candidate matches partially -> Select it. - return createComponentFrom(matchingTemplates.front(), context, arg); - } - else if (!matchingTemplates.empty()) - { - // Ambiguous partial match: Use deduction rules to resolve ambiguity. - auto deducedCandidates = selectCandidatesDeductionRules(matchingTemplates, context, arg); - if (!deducedCandidates.empty()) - { - msg_warning_when(deducedCandidates.size() > 1) << "Multiple candidates with the same templates: " << - sofa::helper::join(deducedCandidates.begin(), deducedCandidates.end(), ","); - return createComponentFrom(deducedCandidates.front(), context, arg); - } - } + return component; } // 4. Attempt General Template Deduction (Lowest Priority). - // If no explicit template matching worked, try to select based purely on deduction rules from all valid candidates. + // If no explicit template matching worked, try to select based purely on deduction rules from + // all valid candidates. + if (auto component = applyFilter(*this, componentName, candidates, context, arg, noFilter)) { - auto deducedCandidates = selectCandidatesDeductionRules(candidates, context, arg); - - if (!deducedCandidates.empty()) - { - msg_warning_when(deducedCandidates.size() > 1) << "Multiple candidates with the same templates: " << - sofa::helper::join(deducedCandidates.begin(), deducedCandidates.end(), ","); - return createComponentFrom(deducedCandidates.front(), context, arg); - } + return component; } // 5. Final fallback + if (!candidates.empty()) { - if (!candidates.empty()) - { - return createComponentFrom(candidates.front(), context, arg); - } + return createComponentFrom(candidates.front(), context, arg); } // Final failure From e4a18f6587eace32b1317586391472147e9dc11b Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sat, 27 Jun 2026 06:32:16 +0200 Subject: [PATCH 25/71] missing branch --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 01004e6d04f..6c84ccfc4c7 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -404,6 +404,11 @@ bool knownIssues(const ComponentFactory& self, const std::string& clasName) using Filter = std::vector (*)(const std::vector&, objectmodel::BaseContext*, objectmodel::BaseObjectDescription*); +/** + * Apply a filter on a list of potential candidates. If the filtered list has a unique element, a + * component will be created based on this element. Otherwise, the filtered list is further filtered + * based on deduction rules. + */ objectmodel::BaseComponent::SPtr applyFilter( const ComponentFactory& self, const std::string& componentName, @@ -435,6 +440,11 @@ objectmodel::BaseComponent::SPtr applyFilter( << "). The first one is selected."; return createComponentFrom(deducedCandidates.front(), context, arg); } + else + { + // None of the deduction rules matches: returning the first filtered candidate + return createComponentFrom(filteredCandidates.front(), context, arg); + } } } return nullptr; From 3a1bd0058b22b440e4652eac68630b4515162fa3 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sat, 27 Jun 2026 06:45:29 +0200 Subject: [PATCH 26/71] module name in legacy mode --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 3 +-- .../Core/src/sofa/core/ComponentRegistrationData.h | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 6c84ccfc4c7..4544da04d46 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -105,8 +105,7 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) component->componentName = ro.m_componentNames[i]; component->aliases = ro.m_aliases; - component->componentNamespace = ""; - component->componentModule = ""; + component->componentModule = ro.m_moduleNames[i]; component->description = ro.m_description; component->authors.insert(ro.m_authors); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 2c1079969a7..17f4a7b459d 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -153,11 +153,17 @@ struct SOFA_CORE_API LegacyComponentRegistrationData } std::vector m_componentNames; + std::vector m_moduleNames; std::vector > m_templateDeductionRules; template LegacyComponentRegistrationData& add(bool = false) { m_componentCreators.push_back(new ComponentCreator); m_componentNames.push_back(T::GetClass()->className); +#ifdef SOFA_TARGET + m_moduleNames.push_back(sofa_tostring(SOFA_TARGET)); +#else + m_moduleNames.emplace_back(); +#endif m_templateDeductionRules.push_back(std::make_shared>()); //to do: deal with templates return *this; From 92e06551bbecd5ce18aa291ecefbf0c2dd416e27 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sat, 27 Jun 2026 06:55:51 +0200 Subject: [PATCH 27/71] secure vector access --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 4544da04d46..bbab34537d7 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -103,9 +103,17 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) auto creator = creators[i]; ComponentDescription::SPtr component = std::make_shared(); + if (i >= ro.m_moduleNames.size()) + { + continue; + } + component->componentName = ro.m_componentNames[i]; component->aliases = ro.m_aliases; - component->componentModule = ro.m_moduleNames[i]; + if (i < ro.m_moduleNames.size()) + { + component->componentModule = ro.m_moduleNames[i]; + } component->description = ro.m_description; component->authors.insert(ro.m_authors); From 1e953acb90741b81890e211ddccb976959498e91 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sat, 27 Jun 2026 07:13:35 +0200 Subject: [PATCH 28/71] add first tests --- Sofa/framework/Core/test/CMakeLists.txt | 1 + .../Core/test/ComponentFactory_test.cpp | 51 ++++ .../Core/test/ObjectFactoryJson_test.cpp | 156 ++++++------ .../Core/test/ObjectFactory_test.cpp | 238 +++++++++--------- 4 files changed, 249 insertions(+), 197 deletions(-) create mode 100644 Sofa/framework/Core/test/ComponentFactory_test.cpp diff --git a/Sofa/framework/Core/test/CMakeLists.txt b/Sofa/framework/Core/test/CMakeLists.txt index 951a6e0701a..e0a0c6f9119 100644 --- a/Sofa/framework/Core/test/CMakeLists.txt +++ b/Sofa/framework/Core/test/CMakeLists.txt @@ -24,6 +24,7 @@ set(SOURCE_FILES objectmodel/VectorData_test.cpp topology/BaseMeshTopology_test.cpp topology/TopologySubsetIndices_test.cpp + ComponentFactory_test.cpp DataEngine_test.cpp Engine_test.cpp MatrixAccumulator_test.cpp diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp new file mode 100644 index 00000000000..6f6aed9ff12 --- /dev/null +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -0,0 +1,51 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include + +namespace sofa +{ + +struct DummyComponent : public core::objectmodel::BaseComponent +{}; + +template +struct DummyComponentWith1Template : public core::objectmodel::BaseComponent +{}; + +template +struct DummyComponentWith2Template : public core::objectmodel::BaseComponent +{}; + +TEST(ComponentFactory, EmptyRegistrationData) +{ + // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT + sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; + EXPECT_MSG_NOEMIT(Error); + EXPECT_MSG_NOEMIT(Warning); + + core::ComponentFactory factory; + factory.registerComponent(core::ComponentRegistrationDataBuilder()); +} + +} diff --git a/Sofa/framework/Core/test/ObjectFactoryJson_test.cpp b/Sofa/framework/Core/test/ObjectFactoryJson_test.cpp index a59bd34718e..90e105c0c64 100644 --- a/Sofa/framework/Core/test/ObjectFactoryJson_test.cpp +++ b/Sofa/framework/Core/test/ObjectFactoryJson_test.cpp @@ -1,78 +1,78 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include - - -namespace sofa -{ -TEST(ObjectFactoryJson, noObject) -{ - core::ObjectFactory o; - const auto dump = core::ObjectFactoryJson::dump(&o); - EXPECT_EQ(dump, "[]"); -} - -TEST(ObjectFactoryJson, oneObject) -{ - core::ObjectFactory o; - - EXPECT_EQ(core::RegisterObject("foo") - .add< simulation::DefaultAnimationLoop >().commitTo(&o), 1); - - const auto dump = core::ObjectFactoryJson::dump(&o); - const std::string expectedDump = R"x([{"className":"DefaultAnimationLoop","creator":{"":{"class":{"categories":["AnimationLoop"],"className":"DefaultAnimationLoop","namespaceName":"sofa::simulation","parents":["BaseAnimationLoop"],"shortName":"defaultAnimationLoop","templateName":"","typeName":"DefaultAnimationLoop"},"object":{"data":[{"defaultValue":"unnamed","group":"","help":"object name","name":"name","type":"string"},{"defaultValue":"0","group":"","help":"if true, emits extra messages at runtime.","name":"printLog","type":"bool"},{"defaultValue":"","group":"","help":"list of the subsets the object belongs to","name":"tags","type":"TagSet"},{"defaultValue":"","group":"","help":"this object bounding box","name":"bbox","type":"BoundingBox"},{"defaultValue":"Undefined","group":"","help":"The state of the component among (Dirty, Valid, Undefined, Loading, Invalid).","name":"componentState","type":"ComponentState"},{"defaultValue":"0","group":"","help":"if true, handle the events, otherwise ignore the events","name":"listening","type":"bool"},{"defaultValue":"1","group":"","help":"If true, compute the global bounding box of the scene at each time step. Used mostly for rendering.","name":"computeBoundingBox","type":"bool"},{"defaultValue":"0","group":"","help":"If true, solves all the ODEs in parallel","name":"parallelODESolving","type":"bool"}],"link":[{"destinationTypeName":"BaseContext","help":"Graph Node containing this object (or BaseContext::getDefault() if no graph is used)","name":"context"},{"destinationTypeName":"BaseComponent","help":"Sub-objects used internally by this object","name":"slaves"},{"destinationTypeName":"BaseComponent","help":"nullptr for regular objects, or master object for which this object is one sub-objects","name":"master"},{"destinationTypeName":"BaseNode","help":"Link to the scene's node that will be processed by the loop","name":"targetNode"}]},"target":""}},"description":"foo\n"}])x"; - EXPECT_EQ(dump, expectedDump); -} - -template -class DummyComponent : public core::objectmodel::BaseComponent -{ -public: - SOFA_CLASS(DummyComponent, BaseObject); -}; - -TEST(ObjectFactoryJson, oneTemplatedObject) -{ - core::ObjectFactory o; - - EXPECT_EQ(core::RegisterObject("foo") - .add< DummyComponent >().commitTo(&o), 1); - - const auto dump = core::ObjectFactoryJson::dump(&o); - const auto vec3name = core::objectmodel::BaseClassNameHelper::getTypeName(); - const std::string expectedDump = R"x([{"className":"DummyComponent","creator":{"Vec3f":{"class":{"categories":["_Miscellaneous"],"className":"DummyComponent","namespaceName":"sofa","parents":["BaseComponent"],"shortName":"dummyComponent","templateName":"Vec3f","typeName":"DummyComponent<)x" + std::string{vec3name} + R"x(>"},"object":{"data":[{"defaultValue":"unnamed","group":"","help":"object name","name":"name","type":"string"},{"defaultValue":"0","group":"","help":"if true, emits extra messages at runtime.","name":"printLog","type":"bool"},{"defaultValue":"","group":"","help":"list of the subsets the object belongs to","name":"tags","type":"TagSet"},{"defaultValue":"","group":"","help":"this object bounding box","name":"bbox","type":"BoundingBox"},{"defaultValue":"Undefined","group":"","help":"The state of the component among (Dirty, Valid, Undefined, Loading, Invalid).","name":"componentState","type":"ComponentState"},{"defaultValue":"0","group":"","help":"if true, handle the events, otherwise ignore the events","name":"listening","type":"bool"}],"link":[{"destinationTypeName":"BaseContext","help":"Graph Node containing this object (or BaseContext::getDefault() if no graph is used)","name":"context"},{"destinationTypeName":"BaseComponent","help":"Sub-objects used internally by this object","name":"slaves"},{"destinationTypeName":"BaseComponent","help":"nullptr for regular objects, or master object for which this object is one sub-objects","name":"master"}]},"target":""}},"description":"foo\n"}])x"; - EXPECT_EQ(dump, expectedDump); -} - -TEST(ObjectFactoryJson, mainInstance) -{ - EXPECT_TRUE(sofa::simpleapi::importPlugin(Sofa.Component)); - const auto dump = core::ObjectFactoryJson::dump(core::ObjectFactory::getInstance()); - EXPECT_NE(dump.find("MechanicalObject"), std::string::npos); -} -} +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// +// +// namespace sofa +// { +// TEST(ObjectFactoryJson, noObject) +// { +// core::ObjectFactory o; +// const auto dump = core::ObjectFactoryJson::dump(&o); +// EXPECT_EQ(dump, "[]"); +// } +// +// TEST(ObjectFactoryJson, oneObject) +// { +// core::ObjectFactory o; +// +// EXPECT_EQ(core::RegisterObject("foo") +// .add< simulation::DefaultAnimationLoop >().commitTo(&o), 1); +// +// const auto dump = core::ObjectFactoryJson::dump(&o); +// const std::string expectedDump = R"x([{"className":"DefaultAnimationLoop","creator":{"":{"class":{"categories":["AnimationLoop"],"className":"DefaultAnimationLoop","namespaceName":"sofa::simulation","parents":["BaseAnimationLoop"],"shortName":"defaultAnimationLoop","templateName":"","typeName":"DefaultAnimationLoop"},"object":{"data":[{"defaultValue":"unnamed","group":"","help":"object name","name":"name","type":"string"},{"defaultValue":"0","group":"","help":"if true, emits extra messages at runtime.","name":"printLog","type":"bool"},{"defaultValue":"","group":"","help":"list of the subsets the object belongs to","name":"tags","type":"TagSet"},{"defaultValue":"","group":"","help":"this object bounding box","name":"bbox","type":"BoundingBox"},{"defaultValue":"Undefined","group":"","help":"The state of the component among (Dirty, Valid, Undefined, Loading, Invalid).","name":"componentState","type":"ComponentState"},{"defaultValue":"0","group":"","help":"if true, handle the events, otherwise ignore the events","name":"listening","type":"bool"},{"defaultValue":"1","group":"","help":"If true, compute the global bounding box of the scene at each time step. Used mostly for rendering.","name":"computeBoundingBox","type":"bool"},{"defaultValue":"0","group":"","help":"If true, solves all the ODEs in parallel","name":"parallelODESolving","type":"bool"}],"link":[{"destinationTypeName":"BaseContext","help":"Graph Node containing this object (or BaseContext::getDefault() if no graph is used)","name":"context"},{"destinationTypeName":"BaseComponent","help":"Sub-objects used internally by this object","name":"slaves"},{"destinationTypeName":"BaseComponent","help":"nullptr for regular objects, or master object for which this object is one sub-objects","name":"master"},{"destinationTypeName":"BaseNode","help":"Link to the scene's node that will be processed by the loop","name":"targetNode"}]},"target":""}},"description":"foo\n"}])x"; +// EXPECT_EQ(dump, expectedDump); +// } +// +// template +// class DummyComponent : public core::objectmodel::BaseComponent +// { +// public: +// SOFA_CLASS(DummyComponent, BaseObject); +// }; +// +// TEST(ObjectFactoryJson, oneTemplatedObject) +// { +// core::ObjectFactory o; +// +// EXPECT_EQ(core::RegisterObject("foo") +// .add< DummyComponent >().commitTo(&o), 1); +// +// const auto dump = core::ObjectFactoryJson::dump(&o); +// const auto vec3name = core::objectmodel::BaseClassNameHelper::getTypeName(); +// const std::string expectedDump = R"x([{"className":"DummyComponent","creator":{"Vec3f":{"class":{"categories":["_Miscellaneous"],"className":"DummyComponent","namespaceName":"sofa","parents":["BaseComponent"],"shortName":"dummyComponent","templateName":"Vec3f","typeName":"DummyComponent<)x" + std::string{vec3name} + R"x(>"},"object":{"data":[{"defaultValue":"unnamed","group":"","help":"object name","name":"name","type":"string"},{"defaultValue":"0","group":"","help":"if true, emits extra messages at runtime.","name":"printLog","type":"bool"},{"defaultValue":"","group":"","help":"list of the subsets the object belongs to","name":"tags","type":"TagSet"},{"defaultValue":"","group":"","help":"this object bounding box","name":"bbox","type":"BoundingBox"},{"defaultValue":"Undefined","group":"","help":"The state of the component among (Dirty, Valid, Undefined, Loading, Invalid).","name":"componentState","type":"ComponentState"},{"defaultValue":"0","group":"","help":"if true, handle the events, otherwise ignore the events","name":"listening","type":"bool"}],"link":[{"destinationTypeName":"BaseContext","help":"Graph Node containing this object (or BaseContext::getDefault() if no graph is used)","name":"context"},{"destinationTypeName":"BaseComponent","help":"Sub-objects used internally by this object","name":"slaves"},{"destinationTypeName":"BaseComponent","help":"nullptr for regular objects, or master object for which this object is one sub-objects","name":"master"}]},"target":""}},"description":"foo\n"}])x"; +// EXPECT_EQ(dump, expectedDump); +// } +// +// TEST(ObjectFactoryJson, mainInstance) +// { +// EXPECT_TRUE(sofa::simpleapi::importPlugin(Sofa.Component)); +// const auto dump = core::ObjectFactoryJson::dump(core::ObjectFactory::getInstance()); +// EXPECT_NE(dump.find("MechanicalObject"), std::string::npos); +// } +// } diff --git a/Sofa/framework/Core/test/ObjectFactory_test.cpp b/Sofa/framework/Core/test/ObjectFactory_test.cpp index b5a3c0e4d3f..fa37532a67e 100644 --- a/Sofa/framework/Core/test/ObjectFactory_test.cpp +++ b/Sofa/framework/Core/test/ObjectFactory_test.cpp @@ -1,119 +1,119 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -using sofa::core::ObjectFactory; -using sofa::core::RegisterObject; - -#include -using sofa::testing::BaseTest ; - -namespace -{ - -template -class TestObject : public sofa::core::objectmodel::BaseComponent -{ -public: - SOFA_CLASS(SOFA_TEMPLATE(TestObject, Type), sofa::core::objectmodel::BaseComponent); -}; - -template -class TestObject2 : public sofa::core::objectmodel::BaseComponent -{ -public: - SOFA_CLASS(SOFA_TEMPLATE(TestObject2, Type), sofa::core::objectmodel::BaseComponent); -}; - -int A = RegisterObject("Dummy test object.") - .add< TestObject >(); -int B1 = RegisterObject("Dummy test object.") - .add< TestObject >(); -int B2 = RegisterObject("Dummy test object.") - .add< TestObject >(); -int B3 = RegisterObject("Dummy test object.") - .add< TestObject2 >(); - -class ObjectFactory_test: public BaseTest -{ -public: - void testDuplicatedRegistration() - { - EXPECT_MSG_EMIT(Warning); - const int C = RegisterObject("Already registered object.") - .add< TestObject >(); - SOFA_UNUSED(C); - } - - void testValidAlias() - { - ASSERT_TRUE(ObjectFactory::getInstance()->addAlias("FirstAlias", "TestObject")); - } - - void testInvalidAlias() - { - EXPECT_MSG_EMIT(Error); - ASSERT_FALSE(ObjectFactory::getInstance()->addAlias("InvalidAlias", "NoWhere")); - } - - void testDuplicatedAlias() - { - EXPECT_MSG_EMIT(Error); - ASSERT_TRUE(ObjectFactory::getInstance()->addAlias("DuplicatedAlias", "TestObject")); - ASSERT_FALSE(ObjectFactory::getInstance()->addAlias("DuplicatedAlias", "TestObject")); - } - - void testHasCreator() - { - // validate the hasCreator method with use of alias when the alias is pointing - // to two different entries in the factory - ASSERT_TRUE(ObjectFactory::HasCreator("TestObject")); - ASSERT_TRUE(ObjectFactory::HasCreator("TestObject2")); - } -}; - -TEST_F(ObjectFactory_test, testDuplicatedRegistration) -{ - this->testDuplicatedRegistration(); -} - -TEST_F(ObjectFactory_test, testValidAlias ) -{ - this->testValidAlias(); -} - -TEST_F(ObjectFactory_test, testInvalidAlias ) -{ - this->testInvalidAlias(); -} - -TEST_F(ObjectFactory_test, testDuplicatedAlias ) -{ - this->testDuplicatedAlias(); -} - - -TEST_F(ObjectFactory_test, testHasCreator ) -{ - this->testHasCreator(); -} - -}// namespace sofa +// /****************************************************************************** +// * SOFA, Simulation Open-Framework Architecture * +// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +// * * +// * This program is free software; you can redistribute it and/or modify it * +// * under the terms of the GNU Lesser General Public License as published by * +// * the Free Software Foundation; either version 2.1 of the License, or (at * +// * your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, but WITHOUT * +// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +// * for more details. * +// * * +// * You should have received a copy of the GNU Lesser General Public License * +// * along with this program. If not, see . * +// ******************************************************************************* +// * Authors: The SOFA Team and external contributors (see Authors.txt) * +// * * +// * Contact information: contact@sofa-framework.org * +// ******************************************************************************/ +// #include +// using sofa::core::ObjectFactory; +// using sofa::core::RegisterObject; +// +// #include +// using sofa::testing::BaseTest ; +// +// namespace +// { +// +// template +// class TestObject : public sofa::core::objectmodel::BaseComponent +// { +// public: +// SOFA_CLASS(SOFA_TEMPLATE(TestObject, Type), sofa::core::objectmodel::BaseComponent); +// }; +// +// template +// class TestObject2 : public sofa::core::objectmodel::BaseComponent +// { +// public: +// SOFA_CLASS(SOFA_TEMPLATE(TestObject2, Type), sofa::core::objectmodel::BaseComponent); +// }; +// +// int A = RegisterObject("Dummy test object.") +// .add< TestObject >(); +// int B1 = RegisterObject("Dummy test object.") +// .add< TestObject >(); +// int B2 = RegisterObject("Dummy test object.") +// .add< TestObject >(); +// int B3 = RegisterObject("Dummy test object.") +// .add< TestObject2 >(); +// +// class ObjectFactory_test: public BaseTest +// { +// public: +// void testDuplicatedRegistration() +// { +// EXPECT_MSG_EMIT(Warning); +// const int C = RegisterObject("Already registered object.") +// .add< TestObject >(); +// SOFA_UNUSED(C); +// } +// +// void testValidAlias() +// { +// ASSERT_TRUE(ObjectFactory::getInstance()->addAlias("FirstAlias", "TestObject")); +// } +// +// void testInvalidAlias() +// { +// EXPECT_MSG_EMIT(Error); +// ASSERT_FALSE(ObjectFactory::getInstance()->addAlias("InvalidAlias", "NoWhere")); +// } +// +// void testDuplicatedAlias() +// { +// EXPECT_MSG_EMIT(Error); +// ASSERT_TRUE(ObjectFactory::getInstance()->addAlias("DuplicatedAlias", "TestObject")); +// ASSERT_FALSE(ObjectFactory::getInstance()->addAlias("DuplicatedAlias", "TestObject")); +// } +// +// void testHasCreator() +// { +// // validate the hasCreator method with use of alias when the alias is pointing +// // to two different entries in the factory +// ASSERT_TRUE(ObjectFactory::HasCreator("TestObject")); +// ASSERT_TRUE(ObjectFactory::HasCreator("TestObject2")); +// } +// }; +// +// TEST_F(ObjectFactory_test, testDuplicatedRegistration) +// { +// this->testDuplicatedRegistration(); +// } +// +// TEST_F(ObjectFactory_test, testValidAlias ) +// { +// this->testValidAlias(); +// } +// +// TEST_F(ObjectFactory_test, testInvalidAlias ) +// { +// this->testInvalidAlias(); +// } +// +// TEST_F(ObjectFactory_test, testDuplicatedAlias ) +// { +// this->testDuplicatedAlias(); +// } +// +// +// TEST_F(ObjectFactory_test, testHasCreator ) +// { +// this->testHasCreator(); +// } +// +// }// namespace sofa From 9c8769e1cc80e7c316c446f09e3b44f236045a0e Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sun, 28 Jun 2026 01:20:52 +0200 Subject: [PATCH 29/71] refacto --- .../linearsolver/direct/SparseLDLSolver.cpp | 9 +- .../fem/elastic/CorotationalFEMForceField.cpp | 11 +- .../statecontainer/MechanicalObject.cpp | 9 +- .../Core/src/sofa/core/ComponentFactory.cpp | 71 +++++----- .../Core/src/sofa/core/ComponentFactory.h | 33 ++--- .../src/sofa/core/ComponentRegistrationData.h | 119 +++++++++++----- .../Core/test/ComponentFactory_test.cpp | 131 ++++++++++++++++-- .../src/sofa/simulation/RequiredPlugin.cpp | 9 +- 8 files changed, 267 insertions(+), 125 deletions(-) diff --git a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp index a59f4765847..77027c74b71 100644 --- a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp +++ b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp @@ -34,12 +34,11 @@ constexpr std::string_view description { "Direct linear solver using a Sparse LD template void registerComponent(sofa::core::ComponentFactory* factory) { - factory->registerComponent>( - core::ComponentRegistrationDataBuilder() - .setName("SparseLDLSolver") + factory->registerComponent( + core::CreateComponent>("SparseLDLSolver") + .withModule(MODULE_NAME) + .withDescription(description) .addTemplateAttribute("matrixType") - .setDescription(std::string(description)) - .setModuleName(MODULE_NAME) ); }; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index deca7f643c7..059bcb00e22 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -34,14 +34,13 @@ static constexpr std::string_view description { "Hooke's law using the corotatio template void registerComponent(sofa::core::ObjectFactory* factory) { - factory->registerComponent>( - core::ComponentRegistrationDataBuilder() - .setName("CorotationalFEMForceField") + factory->registerComponent( + core::CreateComponent>("CorotationalFEMForceField") + .withModule(MODULE_NAME) + .withDescription(std::string(description)) .addTemplateAttribute("dofType") .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) - .setDescription(std::string(description)) - .setModuleName(MODULE_NAME) - .setDeductionRule>() + .withDeductionRule>() ); } diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp index c02d9d46cbe..6d50274f61c 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp @@ -46,12 +46,11 @@ static constexpr std::string_view description {"Mechanical state vectors"}; template void registerComponent(sofa::core::ComponentFactory* factory) { - factory->registerComponent>( - core::ComponentRegistrationDataBuilder() - .setName("MechanicalObject") + factory->registerComponent( + core::CreateComponent>("MechanicalObject") + .withModule(MODULE_NAME) + .withDescription(std::string(description)) .addTemplateAttribute("dofType") - .setDescription(std::string(description)) - .setModuleName(MODULE_NAME) ); }; diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index bbab34537d7..c0601da4a3c 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -101,21 +101,22 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) for (std::size_t i = 0; i < creators.size(); ++i) { auto creator = creators[i]; - ComponentDescription::SPtr component = std::make_shared(); - if (i >= ro.m_moduleNames.size()) + auto component = std::make_shared(); + + if (i < ro.m_componentNames.size()) { - continue; + component->componentName = ro.m_componentNames[i]; } - - component->componentName = ro.m_componentNames[i]; - component->aliases = ro.m_aliases; + component->description = ro.m_description; if (i < ro.m_moduleNames.size()) { - component->componentModule = ro.m_moduleNames[i]; + component->componentName = ro.m_moduleNames[i]; } - component->description = ro.m_description; + component->creator = creator->clone(); + + component->aliases = ro.m_aliases; component->authors.insert(ro.m_authors); component->license = ro.m_license; component->documentationURL.insert(ro.m_documentationURL); @@ -134,7 +135,6 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) } } - component->creator = creator->clone(); component->templateDeductionRule = ro.m_templateDeductionRules[i]; this->m_registry.push_back(component); @@ -148,12 +148,12 @@ namespace { -std::vector getComponentsFromName( +std::vector getComponentsFromName( const ComponentFactory& self, const std::string& componentName, const std::string& pluginName) { - std::vector result; + std::vector result; std::string componentToSearch = componentName; @@ -228,14 +228,14 @@ void autoLoadPlugin(ComponentFactory& self, const std::string& pluginName) } } -std::vector selectCandidatesFromTemplateAttributes( - const std::vector& candidates, +std::vector selectCandidatesFromTemplateAttributes( + const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { SOFA_UNUSED(context); - std::vector exactlyMatchingCandidates; + std::vector exactlyMatchingCandidates; for (const auto& candidate : candidates) { @@ -267,14 +267,14 @@ std::vector selectCandidatesFromTemplateAttributes( return exactlyMatchingCandidates; } -std::vector selectCandidatesFromPartialTemplateAttributes( - const std::vector& candidates, +std::vector selectCandidatesFromPartialTemplateAttributes( + const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { SOFA_UNUSED(context); - std::vector partiallyMatchingCandidates; + std::vector partiallyMatchingCandidates; for (const auto& candidate : candidates) { @@ -296,8 +296,8 @@ std::vector selectCandidatesFromPartialTemplateAttri return partiallyMatchingCandidates; } -std::vector selectCandidatesTemplateKeyword( - const std::vector& candidates, +std::vector selectCandidatesTemplateKeyword( + const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { @@ -310,7 +310,7 @@ std::vector selectCandidatesTemplateKeyword( std::string templateAttrStr { templateAttr }; templateAttrStr = defaulttype::TemplateAliases::resolveAlias(templateAttrStr); - std::vector matchingCandidates; + std::vector matchingCandidates; for (const auto& candidate : candidates) { @@ -326,8 +326,8 @@ std::vector selectCandidatesTemplateKeyword( return matchingCandidates; } -std::vector noFilter( - const std::vector& candidates, +std::vector noFilter( + const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { @@ -336,12 +336,12 @@ std::vector noFilter( return candidates; } -std::vector selectCandidatesDeductionRules( - const std::vector& candidates, +std::vector selectCandidatesDeductionRules( + const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { - std::vector matchingCandidates; + std::vector matchingCandidates; for (const auto& candidate : candidates) { @@ -356,7 +356,7 @@ std::vector selectCandidatesDeductionRules( return matchingCandidates; } -objectmodel::BaseComponent::SPtr createComponentFrom(const ComponentDescription::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +objectmodel::BaseComponent::SPtr createComponentFrom(const ComponentRegistrationData::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { auto component = desc->creator->create(); @@ -379,7 +379,7 @@ std::vector similarComponentNames(const ComponentFactory& self, con { std::set allClassNames; std::transform(self.getRegistry().begin(), self.getRegistry().end(), std::inserter(allClassNames, allClassNames.begin()), - [](const ComponentDescription::SPtr& component){ return component->componentName; }); + [](const ComponentRegistrationData::SPtr& component){ return component->componentName; }); const auto result = sofa::helper::getClosestMatch(className, std::vector(allClassNames.begin(), allClassNames.end()), 5, 0.6); std::vector similarComponentNames; std::transform(result.begin(), result.end(), std::back_inserter(similarComponentNames), @@ -409,7 +409,7 @@ bool knownIssues(const ComponentFactory& self, const std::string& clasName) return false; } -using Filter = std::vector (*)(const std::vector&, objectmodel::BaseContext*, objectmodel::BaseObjectDescription*); +using Filter = std::vector (*)(const std::vector&, objectmodel::BaseContext*, objectmodel::BaseObjectDescription*); /** * Apply a filter on a list of potential candidates. If the filtered list has a unique element, a @@ -419,7 +419,7 @@ using Filter = std::vector (*)(const std::vector& candidates, + const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg, Filter filter) @@ -489,7 +489,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( autoLoadPlugin(*this, moduleName); } - std::vector candidates = getComponentsFromName(*this, componentName, moduleName); + std::vector candidates = getComponentsFromName(*this, componentName, moduleName); // Early failure because there are no compatible candidates if (candidates.empty()) @@ -510,7 +510,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( // Check that the candidates don't rely on unloaded modules { auto candidatesWithoutUnloadedPlugins = candidates; - std::erase_if(candidatesWithoutUnloadedPlugins, [](const ComponentDescription::SPtr& candidate) + std::erase_if(candidatesWithoutUnloadedPlugins, [](const ComponentRegistrationData::SPtr& candidate) { return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); }); @@ -519,7 +519,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( { std::set unloadedPlugins; std::transform(candidates.begin(), candidates.end(), std::inserter(unloadedPlugins, unloadedPlugins.begin()), - [](const ComponentDescription::SPtr& component) { return component->componentModule; }); + [](const ComponentRegistrationData::SPtr& component) { return component->componentModule; }); const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end(), ", "); msg_error() << "Attempted to create component '" << componentName << "' but all potential candidates rely on component from currently unloaded plugins:" << unloadedPluginsString << "]"; @@ -584,7 +584,7 @@ bool ComponentFactory::hasCreator(const std::string& classname) const [&](const auto& component){ return component->componentName == classname; }); } -void ComponentFactory::getEntriesFromTarget(std::vector& result, +void ComponentFactory::getEntriesFromTarget(std::vector& result, const std::string& target) const { for (const auto& component : m_registry) @@ -598,9 +598,10 @@ void ComponentFactory::getEntriesFromTarget(std::vector entries; + std::vector entries; this->getEntriesFromTarget(entries, target); - return sofa::helper::join(entries, separator); + return sofa::helper::join(entries.begin(), entries.end(), + [](const auto& entry) { std::stringstream ss; ss << *entry; return ss.str();}, separator); } ComponentFactory* ComponentFactory::getInstance() { return MainComponentFactory::getInstance(); } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 8d7ba0926ec..d9c01ef2333 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -28,26 +28,14 @@ namespace sofa::core { -struct SOFA_CORE_API ComponentDescription - : public ComponentRegistrationData -{ - using SPtr = std::shared_ptr; - - ComponentDescription() = default; - explicit ComponentDescription(const ComponentRegistrationData& data) - : ComponentRegistrationData(data) {} - - std::unique_ptr creator; -}; - class SOFA_CORE_API ComponentFactory { public: // to deprecate - using ClassEntry = ComponentDescription; + using ClassEntry = ComponentRegistrationData; - using Registry = std::vector; + using Registry = std::vector; const Registry& getRegistry() const { return m_registry; } bool registerObjectsFromPlugin(const std::string& pluginName); @@ -55,12 +43,9 @@ class SOFA_CORE_API ComponentFactory //to deprecate bool registerObjects(LegacyComponentRegistrationData& ro); - template - void registerComponent(const ComponentRegistrationData& componentDescription) + void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData) { - auto componentInRegistry = std::make_shared(componentDescription); - componentInRegistry->creator = std::make_unique>(); - m_registry.push_back(componentInRegistry); + m_registry.push_back(componentRegistrationData); } @@ -76,14 +61,14 @@ class SOFA_CORE_API ComponentFactory bool hasCreator(const std::string& classname) const; /// Fill the given vector with the registered classes from a given target - void getEntriesFromTarget(std::vector& result, const std::string& target) const; + void getEntriesFromTarget(std::vector& result, const std::string& target) const; /// Return the list of classes from a given target std::string listClassesFromTarget(std::string target, std::string separator = ", "); /// Fill the given vector with all the registered classes derived from BaseClass template - std::vector getEntriesDerivedFrom() const; + std::vector getEntriesDerivedFrom() const; /// Return the list of classes derived from BaseClass as a string template @@ -108,9 +93,9 @@ class SOFA_CORE_API ComponentFactory template -std::vector ComponentFactory::getEntriesDerivedFrom() const +std::vector ComponentFactory::getEntriesDerivedFrom() const { - std::vector result; + std::vector result; auto* componentClass = Class::GetClass(); if (!componentClass) @@ -138,7 +123,7 @@ std::string ComponentFactory::listClassesDerivedFrom(const std::string& separato auto entries = getEntriesDerivedFrom(); return sofa::helper::join(entries.begin(), entries.end(), - [](const ComponentDescription::SPtr& entry){ return entry->componentName;}, separator); + [](const ComponentRegistrationData::SPtr& entry){ return entry->componentName;}, separator); } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 17f4a7b459d..0d616a287ab 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -35,6 +35,9 @@ namespace sofa::core struct SOFA_CORE_API ComponentRegistrationData { + friend class ComponentFactory; + using SPtr = std::shared_ptr; + std::string componentName; std::set aliases; @@ -51,42 +54,36 @@ struct SOFA_CORE_API ComponentRegistrationData std::set documentationURL; std::shared_ptr templateDeductionRule; + + std::unique_ptr creator; + +private: + // ComponentRegistrationData() = default; }; -inline std::ostream& operator<<(std::ostream& os, const ComponentRegistrationData& data) +struct SOFA_CORE_API ComponentRegistrationDataBuilder { - os << data.componentName; - if (!data.templateAttributes.empty()) - { - os << "["; - os << sofa::helper::join(data.templateAttributes.begin(), data.templateAttributes.end(), - [](const std::pair& pair) - { - return pair.first + "=" + pair.second; - }, ','); - os << "]"; - } - return os; -} + ComponentRegistrationData::SPtr data; -struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistrationData -{ - ComponentRegistrationDataBuilder& setName(const std::string& _componentName) + ComponentRegistrationDataBuilder(const std::string& componentName, const std::string& moduleName, const std::string& description, std::unique_ptr creator) + : data(std::make_shared()) { - this->componentName = _componentName; - return *this; + data->componentName = componentName; + data->componentModule = moduleName; + data->description = description; + data->creator = std::move(creator); } ComponentRegistrationDataBuilder& addAlias(const std::string& alias) { - this->aliases.insert(alias); + data->aliases.insert(alias); return *this; } ComponentRegistrationDataBuilder& addTemplateAttribute( const std::string& templateAttribute, const std::string& value) { - this->templateAttributes.emplace_back(templateAttribute, value); + data->templateAttributes.emplace_back(templateAttribute, value); return *this; } @@ -96,44 +93,94 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder : public ComponentRegistra return addTemplateAttribute(templateAttribute, T::Name()); } - ComponentRegistrationDataBuilder& setModuleName(const std::string& _moduleName) + ComponentRegistrationDataBuilder& addAuthor(const std::string& _author) { - this->componentModule = _moduleName; + data->authors.insert(_author); return *this; } - ComponentRegistrationDataBuilder& setDescription(const std::string& _description) + ComponentRegistrationDataBuilder& withLicense(const std::string& _license) { - this->description = _description; + data->license = _license; return *this; } - ComponentRegistrationDataBuilder& addAuthor(const std::string& _author) + ComponentRegistrationDataBuilder& withDocumentationURL(const std::string& _documentationURL) { - this->authors.insert(_author); + data->documentationURL.insert(_documentationURL); return *this; } - ComponentRegistrationDataBuilder& setLicense(const std::string& _license) + template + ComponentRegistrationDataBuilder& withDeductionRule() { - this->license = _license; + data->templateDeductionRule = std::make_shared(); return *this; } - ComponentRegistrationDataBuilder& setDocumentationURL(const std::string& _documentationURL) + operator ComponentRegistrationData::SPtr() const { - this->documentationURL.insert(_documentationURL); - return *this; + return data; } +}; - template - ComponentRegistrationDataBuilder& setDeductionRule() +inline std::ostream& operator<<(std::ostream& os, const ComponentRegistrationData& data) +{ + os << data.componentName; + if (!data.templateAttributes.empty()) { - this->templateDeductionRule = std::make_shared(); - return *this; + os << "["; + os << sofa::helper::join(data.templateAttributes.begin(), data.templateAttributes.end(), + [](const std::pair& pair) + { + return pair.first + "=" + pair.second; + }, ','); + os << "]"; + } + return os; +} + +struct SOFA_CORE_API ComponentRegistrationDataModule +{ + ComponentRegistrationDataBuilder withDescription(const std::string& description) + { + return {m_componentName, m_moduleName, description, std::move(m_creator)}; } + ComponentRegistrationDataModule(const std::string& componentName, const std::string& moduleName, std::unique_ptr creator) + : m_componentName(componentName), m_moduleName(moduleName), m_creator(std::move(creator)) {} +private: + std::string m_componentName; + std::string m_moduleName; + std::unique_ptr m_creator; +}; + +struct SOFA_CORE_API ComponentRegistrationDataName +{ + ComponentRegistrationDataModule withModule(const std::string& moduleName) + { + return {m_componentName, moduleName, std::move(m_creator)}; + } + ComponentRegistrationDataName(const std::string& componentName, std::unique_ptr creator) + : m_componentName(componentName) + , m_creator(std::move(creator)) + {} +private: + std::string m_componentName; + std::unique_ptr m_creator; }; +template +ComponentRegistrationDataName CreateComponent(const std::string& componentName) +{ + std::unique_ptr creator = std::make_unique>(); + return ComponentRegistrationDataName(componentName, std::move(creator)); +} + + + +/**************************************************************************************************/ + + //to deprecate struct SOFA_CORE_API LegacyComponentRegistrationData { diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index 6f6aed9ff12..68c55f3ce79 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -21,31 +21,144 @@ ******************************************************************************/ #include #include +#include +#include #include +#include namespace sofa { +// factory.registerComponent(core::CreateComponent("DummyComponent") +// .withModule("test") +// .withDescription("dummy") +// ); struct DummyComponent : public core::objectmodel::BaseComponent -{}; +{ + SOFA_CLASS(DummyComponent, core::objectmodel::BaseComponent); +}; +// factory.registerComponent(core::CreateComponent("DummyComponentWith1Template") +// .withModule("test") +// .withDescription("dummy") +// .addTemplateAttribute("t") +// ); template struct DummyComponentWith1Template : public core::objectmodel::BaseComponent -{}; +{ + SOFA_CLASS(DummyComponentWith1Template, core::objectmodel::BaseComponent); +}; +// factory.registerComponent(core::CreateComponent("DummyComponentWith1Template") +// .withModule("test") +// .withDescription("dummy") +// .addTemplateAttribute("t1") +// .addTemplateAttribute("t2") +// ); template struct DummyComponentWith2Template : public core::objectmodel::BaseComponent -{}; +{ + SOFA_CLASS(SOFA_TEMPLATE2(DummyComponentWith2Template, T1, T2), core::objectmodel::BaseComponent); +}; + -TEST(ComponentFactory, EmptyRegistrationData) +struct ComponentFactory_test : public ::testing::Test { - // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT - sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; - EXPECT_MSG_NOEMIT(Error); - EXPECT_MSG_NOEMIT(Warning); + void SetUp() override + { + sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance()); + node = sofa::simulation::getSimulation()->createNewNode("root"); + } core::ComponentFactory factory; - factory.registerComponent(core::ComponentRegistrationDataBuilder()); + simulation::Node::SPtr node; +}; + +TEST_F(ComponentFactory_test, CreateComponent) +{ + factory.registerComponent(core::CreateComponent("DummyComponent") + .withModule("test") + .withDescription("dummy") + ); + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "DummyComponent"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_EQ(createdComponent->getName(), "nameInTheScene"); + EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); +} + +TEST_F(ComponentFactory_test, HasCreator) +{ + factory.registerComponent(core::CreateComponent("DummyComponent") + .withModule("test") + .withDescription("dummy") + ); + + EXPECT_TRUE(factory.hasCreator("DummyComponent")); + EXPECT_FALSE(factory.hasCreator("NonExistentComponent")); +} + +TEST_F(ComponentFactory_test, Aliases) +{ + factory.registerComponent(core::CreateComponent("DummyComponent") + .withModule("test") + .withDescription("dummy") + .addAlias("DummyAlias") + ); + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "DummyAlias"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); +} + +TEST_F(ComponentFactory_test, TargetEntries) +{ + factory.registerComponent(core::CreateComponent("Compo1") + .withModule("ModuleA") + .withDescription("d1") + ); + factory.registerComponent(core::CreateComponent("Compo2") + .withModule("ModuleA") + .withDescription("d2") + ); + factory.registerComponent(core::CreateComponent("Compo3") + .withModule("ModuleB") + .withDescription("d3") + ); + + std::vector entries; + factory.getEntriesFromTarget(entries, "ModuleA"); + EXPECT_EQ(entries.size(), 2); + + std::string list = factory.listClassesFromTarget("ModuleA"); + EXPECT_TRUE(list.find("Compo1") != std::string::npos); + EXPECT_TRUE(list.find("Compo2") != std::string::npos); + EXPECT_FALSE(list.find("Compo3") != std::string::npos); +} + +TEST_F(ComponentFactory_test, Templates) +{ + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); + desc.setAttribute("template", "int"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); } +TEST_F(ComponentFactory_test, CreateUnknownComponent) +{ + EXPECT_MSG_EMIT(Error); + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "UnknownComponent"); + auto createdComponent = factory.createComponent(node.get(), &desc); + EXPECT_EQ(createdComponent, nullptr); +} + + } diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp index d97372f6700..35e360610b4 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/RequiredPlugin.cpp @@ -35,11 +35,10 @@ namespace sofa::simulation void registerRequiredPlugin(sofa::core::ObjectFactory* factory) { - factory->registerComponent( - sofa::core::ComponentRegistrationDataBuilder() - .setName("RequiredPlugin") - .setDescription("Load the SOFA modules and/or plugins required to run a simulation.") - .setModuleName("Sofa.Simulation.Core") + factory->registerComponent( + sofa::core::CreateComponent("RequiredPlugin") + .withModule("Sofa.Simulation.Core") + .withDescription("Load the SOFA modules and/or plugins required to run a simulation.") ); } From 8d8032d1c5d616d1edd305834277de14474adfc8 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Sun, 28 Jun 2026 11:39:32 +0200 Subject: [PATCH 30/71] illustrate difference --- Sofa/framework/Core/test/ComponentFactory_test.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index 68c55f3ce79..a0218fd0176 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -88,6 +88,20 @@ TEST_F(ComponentFactory_test, CreateComponent) EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); } +TEST_F(ComponentFactory_test, CreateComponentDifferentName) +{ + factory.registerComponent(core::CreateComponent("NotDummyComponent") + .withModule("test") + .withDescription("dummy") + ); + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "NotDummyComponent"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_EQ(createdComponent->getName(), "nameInTheScene"); + EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); +} + TEST_F(ComponentFactory_test, HasCreator) { factory.registerComponent(core::CreateComponent("DummyComponent") From 4cf274738e583bba8123a3c27ffbfa44d1b8fb98 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 29 Jun 2026 10:15:06 +0200 Subject: [PATCH 31/71] define the factory name of a component --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 2 ++ .../framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp | 3 +++ Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h | 1 + Sofa/framework/Core/test/ComponentFactory_test.cpp | 1 + 4 files changed, 7 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index c0601da4a3c..6c5437bd383 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -370,6 +370,8 @@ objectmodel::BaseComponent::SPtr createComponentFrom(const ComponentRegistration msg_warning_when(desc->componentModule.empty(), component.get()) << "Module name is empty"; component->parse(arg); + + component->d_factoryName.setValue(desc->componentName); } return component; diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp index 656955cdd71..86906a1916b 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp @@ -38,6 +38,7 @@ BaseComponent::BaseComponent() , l_context(initLink("context","Graph Node containing this object (or BaseContext::getDefault() if no graph is used)")) , l_slaves(initLink("slaves","Sub-objects used internally by this object")) , l_master(initLink("master","nullptr for regular objects, or master object for which this object is one sub-objects")) + , d_factoryName(initData(&d_factoryName, std::string{}, "factoryName", "Component name registered into the factory")) { auto bindChangeContextLink = [this](auto&& before, auto&& after) { return this->changeContextLink(before, after); }; l_context.setValidator(bindChangeContextLink); @@ -46,6 +47,8 @@ BaseComponent::BaseComponent() auto bindChangeSlavesLink = [this](auto&& ptr, auto&& index, auto&& add) { return this->changeSlavesLink(ptr, index, add); }; l_slaves.setValidator(bindChangeSlavesLink); f_listening.setAutoLink(false); + + d_factoryName.setReadOnly(true); } BaseComponent::~BaseComponent() diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h index 667f91a6e39..c6587786c8e 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h @@ -184,6 +184,7 @@ class SOFA_CORE_API BaseComponent : public virtual Base Base* findLinkDestClass(const BaseClass* destType, const std::string& path, const BaseLink* link) override; + Data d_factoryName; /// Return the full path name of this object virtual std::string getPathName() const override; diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index a0218fd0176..d062d243f54 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -100,6 +100,7 @@ TEST_F(ComponentFactory_test, CreateComponentDifferentName) ASSERT_NE(createdComponent, nullptr); EXPECT_EQ(createdComponent->getName(), "nameInTheScene"); EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); + EXPECT_EQ(createdComponent->d_factoryName.getValue(), "NotDummyComponent"); } TEST_F(ComponentFactory_test, HasCreator) From 2d417f4e027b3143d4d1c04f5cd55c2f2a2bec8a Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 29 Jun 2026 12:17:37 +0200 Subject: [PATCH 32/71] more tests --- .../Core/src/sofa/core/ComponentFactory.cpp | 11 +++- .../Core/test/ComponentFactory_test.cpp | 53 ++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 6c5437bd383..e4e2f76ab63 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -242,6 +242,7 @@ std::vector selectCandidatesFromTemplateAttribu bool matchAllTemplateParameters = true; for (const auto& [attribute, value] : candidate->templateAttributes) { + const auto resolvedValue = defaulttype::TemplateAliases::resolveAlias(value); const char* attr = arg->getAttribute(attribute, nullptr); if (attr == nullptr) { @@ -250,7 +251,8 @@ std::vector selectCandidatesFromTemplateAttribu else { const std::string attrStr { attr }; - if (defaulttype::TemplateAliases::resolveAlias(attrStr) != value) + const auto resolvedAlias = defaulttype::TemplateAliases::resolveAlias(attrStr); + if (resolvedAlias != value) { matchAllTemplateParameters = false; } @@ -451,6 +453,13 @@ objectmodel::BaseComponent::SPtr applyFilter( } else { + msg_warning_when(filteredCandidates.size() > 1, &self) + << "Attempt to create component '" << componentName + << "', however multiple potential candidates match the provided attributes (" + << sofa::helper::join( + filteredCandidates.begin(), filteredCandidates.end(), + [](const ComponentRegistrationData::SPtr& desc){ std::stringstream ss; ss << *desc; return ss.str(); }, ", ") + << "). The first one is selected."; // None of the deduction rules matches: returning the first filtered candidate return createComponentFrom(filteredCandidates.front(), context, arg); } diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index d062d243f54..acd3ec66ba0 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -86,6 +86,7 @@ TEST_F(ComponentFactory_test, CreateComponent) ASSERT_NE(createdComponent, nullptr); EXPECT_EQ(createdComponent->getName(), "nameInTheScene"); EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); + EXPECT_EQ(createdComponent->d_factoryName.getValue(), "DummyComponent"); } TEST_F(ComponentFactory_test, CreateComponentDifferentName) @@ -153,7 +154,7 @@ TEST_F(ComponentFactory_test, TargetEntries) EXPECT_FALSE(list.find("Compo3") != std::string::npos); } -TEST_F(ComponentFactory_test, Templates) +TEST_F(ComponentFactory_test, OneTemplate_attributes) { factory.registerComponent(core::CreateComponent>("TemplatedCompo") .withModule("test") @@ -161,10 +162,60 @@ TEST_F(ComponentFactory_test, Templates) .addTemplateAttribute("t", "int") ); + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "float") + ); + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); + desc.setAttribute("t", "int"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_NE(dynamic_cast*>(createdComponent.get()), nullptr); +} + +TEST_F(ComponentFactory_test, OneTemplate_identicalRegistration) +{ + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); + desc.setAttribute("t", "int"); + EXPECT_MSG_EMIT(Warning); //ambiguity because of identical registration + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_NE(dynamic_cast*>(createdComponent.get()), nullptr); +} + +TEST_F(ComponentFactory_test, OneTemplate_templateKeyword) +{ + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); desc.setAttribute("template", "int"); auto createdComponent = factory.createComponent(node.get(), &desc); ASSERT_NE(createdComponent, nullptr); + EXPECT_NE(dynamic_cast*>(createdComponent.get()), nullptr); } TEST_F(ComponentFactory_test, CreateUnknownComponent) From fe972a555002a9111ac91041437fa39c17dff5a0 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 29 Jun 2026 12:34:49 +0200 Subject: [PATCH 33/71] more messages --- .../Core/src/sofa/core/ComponentFactory.cpp | 26 ++++++++++++++++ .../Core/src/sofa/core/ComponentFactory.h | 6 +--- .../Core/test/ComponentFactory_test.cpp | 31 +++++++++++++++++-- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index e4e2f76ab63..b73e93357f0 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -143,6 +143,32 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) return true; } +void ComponentFactory::registerComponent( + const ComponentRegistrationData::SPtr& componentRegistrationData) +{ + //check no duplicate + for (const auto& component : m_registry) + { + if (component->componentName == componentRegistrationData->componentName) + { + auto allTemplateAttributes = true; + for (const auto& templateAttribute : componentRegistrationData->templateAttributes) + { + if (std::find(component->templateAttributes.begin(), component->templateAttributes.end(), + templateAttribute) == component->templateAttributes.end()) + { + allTemplateAttributes = false; + } + } + if (!componentRegistrationData->templateAttributes.empty() && allTemplateAttributes) + { + msg_error() << "Attempt to register a new component in the factory with identical attributes than " << *component; + } + } + } + + m_registry.push_back(componentRegistrationData); +} namespace { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index d9c01ef2333..f28640d5530 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -43,11 +43,7 @@ class SOFA_CORE_API ComponentFactory //to deprecate bool registerObjects(LegacyComponentRegistrationData& ro); - void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData) - { - m_registry.push_back(componentRegistrationData); - } - + void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData); /// Create a component given a context and a description. objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index acd3ec66ba0..4e6b321faeb 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -183,14 +183,41 @@ TEST_F(ComponentFactory_test, OneTemplate_identicalRegistration) .addTemplateAttribute("t", "int") ); - factory.registerComponent(core::CreateComponent>("TemplatedCompo") + { + EXPECT_MSG_EMIT(Error); + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + } + + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); + desc.setAttribute("t", "int"); + EXPECT_MSG_EMIT(Warning); //ambiguity because of identical registration + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_NE(dynamic_cast*>(createdComponent.get()), nullptr); +} + +TEST_F(ComponentFactory_test, OneTemplate_identicalRegistrationNoTemplateAttribute) +{ + factory.registerComponent(core::CreateComponent>("TemplatedCompo") .withModule("test") .withDescription("dummy") .addTemplateAttribute("t", "int") ); + { + EXPECT_MSG_EMIT(Error); + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t", "int") + ); + } + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); - desc.setAttribute("t", "int"); EXPECT_MSG_EMIT(Warning); //ambiguity because of identical registration auto createdComponent = factory.createComponent(node.get(), &desc); ASSERT_NE(createdComponent, nullptr); From ba48254dc78c11aa58198cea41574a774df1a34d Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 29 Jun 2026 12:40:52 +0200 Subject: [PATCH 34/71] factorization in a loop --- .../Core/src/sofa/core/ComponentFactory.cpp | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index b73e93357f0..24b4c4df30a 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -569,36 +569,20 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - // 1. Attempt Exact Template Match (Highest Priority). - if (auto component = applyFilter(*this, componentName, candidates, context, arg, - selectCandidatesFromTemplateAttributes)) - { - return component; - } - - // 2. Attempt Selection by Legacy 'template' Keyword (Medium-High Priority). - if (auto component = applyFilter(*this, componentName, candidates, context, arg, - selectCandidatesTemplateKeyword)) - { - return component; - } - - // 3. Attempt Partial Template Matching (Medium Priority). - if (auto component = applyFilter(*this, componentName, candidates, context, arg, - selectCandidatesFromPartialTemplateAttributes)) - { - return component; - } - - // 4. Attempt General Template Deduction (Lowest Priority). - // If no explicit template matching worked, try to select based purely on deduction rules from - // all valid candidates. - if (auto component = applyFilter(*this, componentName, candidates, context, arg, noFilter)) - { - return component; + for (const Filter& filter : { + selectCandidatesFromTemplateAttributes, //Exact Template Match (Highest Priority). + selectCandidatesTemplateKeyword, //Selection by Legacy 'template' Keyword (Medium-High Priority) + selectCandidatesFromPartialTemplateAttributes, //Partial Template Matching (Medium Priority) + noFilter //General Template Deduction (Lowest Priority) + }) + { + if (auto component = applyFilter(*this, componentName, candidates, context, arg, filter)) + { + return component; + } } - // 5. Final fallback + // Final fallback if (!candidates.empty()) { return createComponentFrom(candidates.front(), context, arg); From 7075e5f42fb9b64d71821bb336539bc1581b23ac Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 29 Jun 2026 14:00:17 +0200 Subject: [PATCH 35/71] fix --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 24b4c4df30a..feaa696575a 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -344,7 +344,7 @@ std::vector selectCandidatesTemplateKeyword( { const auto templateList = sofa::helper::join( candidate->templateAttributes.begin(), candidate->templateAttributes.end(), - [](const auto& attr){ return attr.second; }, ','); + [](const auto& attr){ return defaulttype::TemplateAliases::resolveAlias(attr.second); }, ','); if (templateAttrStr == templateList) { matchingCandidates.push_back(candidate); From d89c4e3d69453d7314044befaf4e0037073c2f71 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Mon, 29 Jun 2026 14:00:23 +0200 Subject: [PATCH 36/71] more tests --- .../Core/test/ComponentFactory_test.cpp | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index 4e6b321faeb..4d12e25d2a5 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -245,6 +245,69 @@ TEST_F(ComponentFactory_test, OneTemplate_templateKeyword) EXPECT_NE(dynamic_cast*>(createdComponent.get()), nullptr); } +TEST_F(ComponentFactory_test, TwoTemplate_attributes) +{ + EXPECT_MSG_NOEMIT(Warning); + EXPECT_MSG_NOEMIT(Error); + + // Register component using two templates (e.g., int and float attributes) + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t1", "int") + .addTemplateAttribute("t2", "float") + ); + + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t1", "int") + .addTemplateAttribute("t2", "float") + ); + + // Create component specifying both template attributes + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); + desc.setAttribute("t1", "int"); + desc.setAttribute("t2", "float"); + auto createdComponent = factory.createComponent(node.get(), &desc); + + ASSERT_NE(createdComponent, nullptr); + // Check if the component is correctly cast to the expected template type + auto* cast = dynamic_cast*>(createdComponent.get()); + EXPECT_NE(cast, nullptr); +} + +TEST_F(ComponentFactory_test, TwoTemplate_templateKeyword) +{ + EXPECT_MSG_NOEMIT(Warning); + EXPECT_MSG_NOEMIT(Error); + + // Register two different components using two templates + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t1", "int") + .addTemplateAttribute("t2", "float") + ); + + factory.registerComponent(core::CreateComponent>("TemplatedCompo") + .withModule("test") + .withDescription("dummy") + .addTemplateAttribute("t1", "double") + .addTemplateAttribute("t2", "bool") + ); + + // Use the 'template' keyword to specify all template arguments: t1=int, t2=float + core::objectmodel::BaseObjectDescription desc("nameInTheScene", "TemplatedCompo"); + desc.setAttribute("template", "int,float"); // Assuming comma-separated list of attributes + auto createdComponent = factory.createComponent(node.get(), &desc); + + ASSERT_NE(createdComponent, nullptr); + // Check if the component is correctly cast to the expected template type + auto* cast = dynamic_cast*>(createdComponent.get()); + EXPECT_NE(cast, nullptr); +} + TEST_F(ComponentFactory_test, CreateUnknownComponent) { EXPECT_MSG_EMIT(Error); From 88e9c46c689807b2220722f532349822863da0a3 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 09:52:54 +0200 Subject: [PATCH 37/71] more tests and fixes --- .../Core/src/sofa/core/ComponentFactory.cpp | 2 +- .../src/sofa/core/ComponentRegistrationData.h | 6 ++ .../Core/test/ComponentFactory_test.cpp | 68 ++++++++++++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index feaa696575a..cfdd81e1889 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -278,7 +278,7 @@ std::vector selectCandidatesFromTemplateAttribu { const std::string attrStr { attr }; const auto resolvedAlias = defaulttype::TemplateAliases::resolveAlias(attrStr); - if (resolvedAlias != value) + if (resolvedAlias != resolvedValue) { matchAllTemplateParameters = false; } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 0d616a287ab..8ab8872379f 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -118,6 +118,12 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder return *this; } + ComponentRegistrationDataBuilder& withInstantiationPriority(unsigned int instantiationPriority) + { + data->instantiationPriority = instantiationPriority; + return *this; + } + operator ComponentRegistrationData::SPtr() const { return data; diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index 4d12e25d2a5..7597586710f 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -262,7 +262,7 @@ TEST_F(ComponentFactory_test, TwoTemplate_attributes) .withModule("test") .withDescription("dummy") .addTemplateAttribute("t1", "int") - .addTemplateAttribute("t2", "float") + .addTemplateAttribute("t2", "string") ); // Create component specifying both template attributes @@ -316,5 +316,71 @@ TEST_F(ComponentFactory_test, CreateUnknownComponent) EXPECT_EQ(createdComponent, nullptr); } +TEST_F(ComponentFactory_test, InstantiationPriority) +{ + auto lowPriority = core::CreateComponent("PriorityCompo") + .withModule("test") + .withDescription("low") + .withInstantiationPriority(1); + factory.registerComponent(lowPriority); + + auto highPriority = core::CreateComponent("PriorityCompo") + .withModule("test") + .withDescription("high") + .withInstantiationPriority(10); + factory.registerComponent(highPriority); + + core::objectmodel::BaseObjectDescription desc("name", "PriorityCompo"); + auto createdComponent = factory.createComponent(node.get(), &desc); + + ASSERT_NE(createdComponent, nullptr); + // The one with description "high" (priority 10) should be selected. + // Since both are DummyComponent, we check the factory data if available or just ensure it doesn't crash. + // In ComponentFactory.cpp, it sorts by priority descending. + EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); +} + +TEST_F(ComponentFactory_test, FullNameCreation) +{ + factory.registerComponent(core::CreateComponent("MyComponent") + .withModule("MyModule") + .withDescription("desc") + ); + + // Create using "Module.Component" syntax + core::objectmodel::BaseObjectDescription desc("name", "MyModule.MyComponent"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); + EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); + EXPECT_EQ(createdComponent->d_factoryName.getValue(), "MyComponent"); +} + +TEST_F(ComponentFactory_test, SuggestionOnMisspell) +{ + factory.registerComponent(core::CreateComponent("SpecificComponent") + .withModule("test").withDescription("description") + ); + + EXPECT_MSG_EMIT(Error); // Should contain suggestion + core::objectmodel::BaseObjectDescription desc("name", "SpecificComponnt"); // Typos + auto createdComponent = factory.createComponent(node.get(), &desc); + EXPECT_EQ(createdComponent, nullptr); +} + +TEST_F(ComponentFactory_test, TemplateAliasResolution) +{ + // Sofa often uses aliases like 'Vec3d' for 'std::vector' etc. + // Here we simulate a registration with a specific type and creation with an alias. + factory.registerComponent(core::CreateComponent>("AliasedCompo") + .withModule("test") + .withDescription("description") + .addTemplateAttribute("t", "d") + ); + + core::objectmodel::BaseObjectDescription desc("name", "AliasedCompo"); + desc.setAttribute("t", "double"); + auto createdComponent = factory.createComponent(node.get(), &desc); + ASSERT_NE(createdComponent, nullptr); +} } From 0037609e18c2ec6ea55c7844c7cc627359fc0d33 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 10:01:00 +0200 Subject: [PATCH 38/71] deprecate functions --- .../Core/src/sofa/core/ComponentFactory.h | 17 +++++++-------- Sofa/framework/Core/src/sofa/core/config.h.in | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index f28640d5530..30ca9d5b962 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -32,27 +32,18 @@ class SOFA_CORE_API ComponentFactory { public: - // to deprecate - using ClassEntry = ComponentRegistrationData; + using SOFA_CORE_DEPRECATED_OBJECTFACTORY_CLASSENTRY() ClassEntry = ComponentRegistrationData; using Registry = std::vector; const Registry& getRegistry() const { return m_registry; } bool registerObjectsFromPlugin(const std::string& pluginName); - //to deprecate - bool registerObjects(LegacyComponentRegistrationData& ro); - void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData); /// Create a component given a context and a description. objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - // to deprecate - objectmodel::BaseComponent::SPtr createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - - - /// Test if a creator exists for a given classname bool hasCreator(const std::string& classname) const; @@ -71,6 +62,12 @@ class SOFA_CORE_API ComponentFactory std::string listClassesDerivedFrom(const std::string& separator = ", ") const; + SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() + objectmodel::BaseComponent::SPtr createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + + SOFA_CORE_DEPRECATED_OBJECTFACTORY_REGISTEROBJECTS() + bool registerObjects(LegacyComponentRegistrationData& ro); + static SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::getInstance instead") ComponentFactory* getInstance(); static SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::CreateComponent instead") objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); static bool SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::HasCreator instead") HasCreator(const std::string& classname); diff --git a/Sofa/framework/Core/src/sofa/core/config.h.in b/Sofa/framework/Core/src/sofa/core/config.h.in index 2215e894724..eab664ab761 100644 --- a/Sofa/framework/Core/src/sofa/core/config.h.in +++ b/Sofa/framework/Core/src/sofa/core/config.h.in @@ -127,3 +127,24 @@ SOFA_ATTRIBUTE_DEPRECATED("v26.06", "v29.06", "Use toBaseComponent instead.") #define SOFA_CORE_DEPRECATED_RENAME_CREATORMAP_OBJECTTEMPLATECREATORMAP() \ SOFA_ATTRIBUTE_DISABLED("v25.12", "v26.06", "Type CreatorMap has been renamed to ObjectTemplateCreatorMap.") #endif + +#ifdef SOFA_BUILD_SOFA_CORE + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_CLASSENTRY() +#else + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_CLASSENTRY() \ + SOFA_ATTRIBUTE_DISABLED("v26.12", "v27.06", "Type ClassEntry has been replaced by ComponentRegistrationData.") +#endif + +#ifdef SOFA_BUILD_SOFA_CORE + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_REGISTEROBJECTS() +#else + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_REGISTEROBJECTS() \ + SOFA_ATTRIBUTE_DISABLED("v26.12", "v27.06", "Use registerComponent instead.") +#endif + +#ifdef SOFA_BUILD_SOFA_CORE + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() +#else + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() \ + SOFA_ATTRIBUTE_DISABLED("v26.12", "v27.06", "Use createComponent instead.") +#endif From df46e515300153634013188694f4e261d145b609 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 10:09:57 +0200 Subject: [PATCH 39/71] more deprecation --- .../Core/src/sofa/core/ComponentRegistrationData.h | 2 +- Sofa/framework/Core/src/sofa/core/config.h.in | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 8ab8872379f..31b624ee0ee 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -188,7 +188,7 @@ ComponentRegistrationDataName CreateComponent(const std::string& componentName) //to deprecate -struct SOFA_CORE_API LegacyComponentRegistrationData +struct SOFA_CORE_DEPRECATED_OBJECTFACTORY_LEGACYREGISTRATIONDATA() SOFA_CORE_API LegacyComponentRegistrationData { std::string m_description; diff --git a/Sofa/framework/Core/src/sofa/core/config.h.in b/Sofa/framework/Core/src/sofa/core/config.h.in index eab664ab761..ccee0106f27 100644 --- a/Sofa/framework/Core/src/sofa/core/config.h.in +++ b/Sofa/framework/Core/src/sofa/core/config.h.in @@ -148,3 +148,10 @@ SOFA_ATTRIBUTE_DEPRECATED("v26.06", "v29.06", "Use toBaseComponent instead.") #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() \ SOFA_ATTRIBUTE_DISABLED("v26.12", "v27.06", "Use createComponent instead.") #endif + +#ifdef SOFA_BUILD_SOFA_CORE + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_LEGACYREGISTRATIONDATA() +#else + #define SOFA_CORE_DEPRECATED_OBJECTFACTORY_LEGACYREGISTRATIONDATA() \ + SOFA_ATTRIBUTE_DISABLED("v26.12", "v27.06", "Use CreateComponent instead.") +#endif From 9858bdf9d2226d5b019aeadd78cb66a24bc5e8c9 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 10:24:24 +0200 Subject: [PATCH 40/71] fix legacy support --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 10 +++++++++- .../Core/src/sofa/core/ComponentRegistrationData.h | 9 ++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index cfdd81e1889..3faaab60f11 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -111,7 +111,15 @@ bool ComponentFactory::registerObjects(LegacyComponentRegistrationData& ro) component->description = ro.m_description; if (i < ro.m_moduleNames.size()) { - component->componentName = ro.m_moduleNames[i]; + component->componentModule = ro.m_moduleNames[i]; + } + if (i < ro.m_instantiationPriority.size()) + { + component->instantiationPriority = ro.m_instantiationPriority[i]; + } + if (i < ro.m_componentTemplates.size()) + { + component->templateAttributes.emplace_back("template", ro.m_componentTemplates[i]); } component->creator = creator->clone(); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 31b624ee0ee..461cc741175 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -206,19 +206,22 @@ struct SOFA_CORE_DEPRECATED_OBJECTFACTORY_LEGACYREGISTRATIONDATA() SOFA_CORE_API } std::vector m_componentNames; + std::vector m_componentTemplates; std::vector m_moduleNames; + std::vector m_instantiationPriority; std::vector > m_templateDeductionRules; - template LegacyComponentRegistrationData& add(bool = false) + template LegacyComponentRegistrationData& add(bool defaultTemplate = false) { m_componentCreators.push_back(new ComponentCreator); - m_componentNames.push_back(T::GetClass()->className); + m_componentNames.push_back(sofa::core::objectmodel::BaseClassNameHelper::getClassName()); + m_componentTemplates.push_back(sofa::core::objectmodel::BaseClassNameHelper::getTemplateName()); #ifdef SOFA_TARGET m_moduleNames.push_back(sofa_tostring(SOFA_TARGET)); #else m_moduleNames.emplace_back(); #endif m_templateDeductionRules.push_back(std::make_shared>()); - //to do: deal with templates + m_instantiationPriority.push_back(std::numeric_limits::max() * defaultTemplate); return *this; } From d3b85fe73824937797f7237f8df0856994482798 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 10:29:05 +0200 Subject: [PATCH 41/71] test legacy support --- .../Core/test/ComponentFactory_test.cpp | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index 7597586710f..9c370651446 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -383,4 +383,98 @@ TEST_F(ComponentFactory_test, TemplateAliasResolution) ASSERT_NE(createdComponent, nullptr); } + + + + + + + + + + + +TEST_F(ComponentFactory_test, LegacyRegistration) +{ + core::LegacyComponentRegistrationData legacyData("legacy description"); + legacyData.add(); + legacyData.addAlias("LegacyAlias"); + legacyData.addAuthor("Legacy Author"); + legacyData.addLicense("LGPL"); + + bool registered = factory.registerObjects(legacyData); + EXPECT_TRUE(registered); + + // Test creation by class name + { + core::objectmodel::BaseObjectDescription desc("name1", "DummyComponent"); + auto created = factory.createComponent(node.get(), &desc); + ASSERT_NE(created, nullptr); + EXPECT_EQ(created->getClassName(), "DummyComponent"); + } + + // Test creation by alias + { + core::objectmodel::BaseObjectDescription desc("name2", "LegacyAlias"); + auto created = factory.createComponent(node.get(), &desc); + ASSERT_NE(created, nullptr); + EXPECT_EQ(created->getClassName(), "DummyComponent"); + } + + // Verify metadata in registry + const auto& registry = factory.getRegistry(); + auto it = std::find_if(registry.begin(), registry.end(), [](const auto& reg) + { + return reg->componentName == "DummyComponent"; + }); + ASSERT_NE(it, registry.end()); + EXPECT_EQ((*it)->description, "legacy description"); + EXPECT_TRUE((*it)->authors.contains("Legacy Author")); + EXPECT_EQ((*it)->license, "LGPL"); +} + +TEST_F(ComponentFactory_test, LegacyRegistrationTemplated) +{ + core::LegacyComponentRegistrationData legacyData("templated legacy"); + legacyData.add>(); + + factory.registerObjects(legacyData); + + core::objectmodel::BaseObjectDescription desc("name", "DummyComponentWith1Template"); + desc.setAttribute("template", "int"); + auto created = factory.createComponent(node.get(), &desc); + ASSERT_NE(created, nullptr); + EXPECT_NE(dynamic_cast*>(created.get()), nullptr); +} + +TEST_F(ComponentFactory_test, LegacyRegistrationTwoTemplates) +{ + core::LegacyComponentRegistrationData legacyData("two templates legacy"); + legacyData.add>(); + + factory.registerObjects(legacyData); + + { + core::objectmodel::BaseObjectDescription desc("name", "DummyComponentWith2Template"); + desc.setAttribute("template", "int,float"); + auto created = factory.createComponent(node.get(), &desc); + + ASSERT_NE(created, nullptr); + auto* cast = dynamic_cast*>(created.get()); + EXPECT_NE(cast, nullptr); + } + + { + core::objectmodel::BaseObjectDescription desc("name", "DummyComponentWith2Template"); + desc.setAttribute("template", "i,f"); + auto created = factory.createComponent(node.get(), &desc); + + ASSERT_NE(created, nullptr); + auto* cast = dynamic_cast*>(created.get()); + EXPECT_NE(cast, nullptr); + } +} + + + } From d931f36675c7e60f74707456bc1e62215a32139e Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 13:59:33 +0200 Subject: [PATCH 42/71] fix compilation --- Sofa/framework/Core/CMakeLists.txt | 1 + .../src/sofa/core/behavior/BaseController.cpp | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 Sofa/framework/Core/src/sofa/core/behavior/BaseController.cpp diff --git a/Sofa/framework/Core/CMakeLists.txt b/Sofa/framework/Core/CMakeLists.txt index 0df4d035b3a..242116f48e5 100644 --- a/Sofa/framework/Core/CMakeLists.txt +++ b/Sofa/framework/Core/CMakeLists.txt @@ -261,6 +261,7 @@ set(SOURCE_FILES ${SRC_ROOT}/behavior/BaseAnimationLoop.cpp ${SRC_ROOT}/behavior/BaseConstraintCorrection.cpp ${SRC_ROOT}/behavior/BaseConstraintSet.cpp + ${SRC_ROOT}/behavior/BaseController.cpp ${SRC_ROOT}/behavior/BaseForceField.cpp ${SRC_ROOT}/behavior/BaseInteractionForceField.cpp ${SRC_ROOT}/behavior/BaseLagrangianConstraint.cpp diff --git a/Sofa/framework/Core/src/sofa/core/behavior/BaseController.cpp b/Sofa/framework/Core/src/sofa/core/behavior/BaseController.cpp new file mode 100644 index 00000000000..97d6fa96a45 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/behavior/BaseController.cpp @@ -0,0 +1,22 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include From c81cd67c1aa1e3fb19b766c45c45cceedb87db94 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 14:19:53 +0200 Subject: [PATCH 43/71] documentation --- .../Core/src/sofa/core/ComponentCreator.h | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h index f842c1196e3..24a9ba647a5 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h @@ -28,14 +28,43 @@ namespace sofa::core { +/** + * @brief Base class for component instantiation. + * + * This abstract interface is used by ComponentFactory to interact with component creators + * without knowing the specific concrete type of the component being created. + */ struct SOFA_CORE_API BaseComponentCreator { virtual ~BaseComponentCreator() = default; + + /** + * @brief Instantiates a new component. + * Used by ComponentFactory to generate object instances before attribute parsing. + * @return A shared pointer to the newly created BaseComponent. + */ virtual objectmodel::BaseComponent::SPtr create() const = 0; + + /** + * @brief Returns the SOFA class metadata associated with the component. + */ virtual const objectmodel::BaseClass* getClass() = 0; + + /** + * @brief Creates a copy of the creator. + * Ensures the factory registry owns unique instances of creators during registration. + */ virtual std::unique_ptr clone() const = 0; }; +/** + * @brief Templated implementation of BaseComponentCreator. + * + * Binds a specific component class to the factory mechanism, ensuring components + * are created using SOFA's memory management (sofa::core::objectmodel::New). + * + * @tparam RealComponent The concrete component class to instantiate. + */ template struct ComponentCreator : public BaseComponentCreator { From 83f94a2062aa34b0090079d1183e70609d6567d2 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 14:38:51 +0200 Subject: [PATCH 44/71] fix --- .../src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp index 77027c74b71..4b80f5ec045 100644 --- a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp +++ b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp @@ -37,7 +37,7 @@ void registerComponent(sofa::core::ComponentFactory* factory) factory->registerComponent( core::CreateComponent>("SparseLDLSolver") .withModule(MODULE_NAME) - .withDescription(description) + .withDescription(std::string(description)) .addTemplateAttribute("matrixType") ); }; From 3ea17aae7e0c3b014d014a64533b730c4df03ede Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 14:45:42 +0200 Subject: [PATCH 45/71] add warning comment --- Sofa/framework/Core/src/sofa/core/ComponentCreator.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h index 24a9ba647a5..663e876f587 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h @@ -70,6 +70,8 @@ struct ComponentCreator : public BaseComponentCreator { objectmodel::BaseComponent::SPtr create() const override { + // WARNING: + // It obliges the class to have a default constructor return sofa::core::objectmodel::New(); } From fa542347c3ea421466f611c805beda399ac8e18c Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Tue, 30 Jun 2026 14:47:18 +0200 Subject: [PATCH 46/71] fix empty constructor --- .../Analyze/src/sofa/component/engine/analyze/Distances.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sofa/Component/Engine/Analyze/src/sofa/component/engine/analyze/Distances.h b/Sofa/Component/Engine/Analyze/src/sofa/component/engine/analyze/Distances.h index 6dd41130825..40a1971067d 100644 --- a/Sofa/Component/Engine/Analyze/src/sofa/component/engine/analyze/Distances.h +++ b/Sofa/Component/Engine/Analyze/src/sofa/component/engine/analyze/Distances.h @@ -70,7 +70,9 @@ class Distances : public core::DataEngine DistancesInternalData data; friend class DistancesInternalData; - Distances ( sofa::component::topology::container::dynamic::DynamicSparseGridTopologyContainer* hexaTopoContainer, core::behavior::MechanicalState* targetPointSet ); + explicit Distances( + topology::container::dynamic::DynamicSparseGridTopologyContainer* hexaTopoContainer = nullptr, + core::behavior::MechanicalState* targetPointSet = nullptr ); ~Distances() override {} From 9a039082841a72d2da2a587dcc234f0914a1cce3 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 11:51:22 +0200 Subject: [PATCH 47/71] remove ComponentLibrary and remove most of CategoryLibrary --- .../Core/src/sofa/core/CategoryLibrary.cpp | 392 +++++++----------- .../Core/src/sofa/core/CategoryLibrary.h | 154 ++++--- .../Core/src/sofa/core/ComponentLibrary.cpp | 138 ++---- .../Core/src/sofa/core/ComponentLibrary.h | 94 ++--- 4 files changed, 288 insertions(+), 490 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp index f84a638cc97..36f8bb708c0 100644 --- a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp +++ b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.cpp @@ -1,234 +1,158 @@ -// /****************************************************************************** -// * SOFA, Simulation Open-Framework Architecture * -// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -// * * -// * This program is free software; you can redistribute it and/or modify it * -// * under the terms of the GNU Lesser General Public License as published by * -// * the Free Software Foundation; either version 2.1 of the License, or (at * -// * your option) any later version. * -// * * -// * This program is distributed in the hope that it will be useful, but WITHOUT * -// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -// * for more details. * -// * * -// * You should have received a copy of the GNU Lesser General Public License * -// * along with this program. If not, see . * -// ******************************************************************************* -// * Authors: The SOFA Team and external contributors (see Authors.txt) * -// * * -// * Contact information: contact@sofa-framework.org * -// ******************************************************************************/ -// -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// -// namespace sofa::core -// { -// -// -// //------------------------------------------------------------------------------------------------------- -// CategoryLibrary::CategoryLibrary( const std::string &categoryName): name(categoryName) -// { -// } -// -// -// ComponentLibrary *CategoryLibrary::addComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) -// { -// //Special case of Mapping and MechanicalMapping -// const bool isMechanicalMapping = (name == "MechanicalMapping"); -// const bool isMapping = (name == "Mapping"); -// -// ComponentLibrary* component = createComponent(componentName, entry, exampleFiles); -// -// //Add the corresponding templates -// std::map::iterator itTemplate; -// -// //It exists Mappings only Mechanical or only Visual. So, we must add the component if only a creator is available for the current category -// bool componentCreationPossible=false; -// //read all the template possible, and remove unused (for Mapping processing) -// std::list templates; -// for (itTemplate=entry->creatorMap.begin(); itTemplate!= entry->creatorMap.end(); ++itTemplate) -// { -// const std::string &templateName = itTemplate->first; -// //If the component corresponds to a MechanicalMapping, we must remove the template related to the visual mapping -// if (isMechanicalMapping) -// { -// const std::string nonMechanical = templateName.substr(0,7); -// if (nonMechanical == "Mapping") continue; -// } -// //If the component corresponds to a Mapping, we must remove the template related to the Mechanical Mapping -// else if (isMapping) -// { -// const std::string mechanical = templateName.substr(0,17); -// if (mechanical == "MechanicalMapping") continue; -// } -// componentCreationPossible=true; -// //component->addTemplate(itTemplate->first); -// if (templateName == (entry->defaultTemplate.empty() ? std::string("Vec3d") : entry->defaultTemplate)) -// templates.push_front(templateName); // make sure the default template is first -// else -// templates.push_back(templateName); -// } -// for (std::list::const_iterator it = templates.begin(); it != templates.end(); ++it) -// component->addTemplate(*it); -// component->endConstruction(); -// -// //If no constructor is available, we delete the component -// if (!componentCreationPossible) -// { -// delete component; -// component=nullptr; -// } -// else -// components.push_back(component); -// -// return component; -// } -// -// void CategoryLibrary::endConstruction() -// { -// } -// -// -// const ComponentLibrary *CategoryLibrary::getComponent( const std::string &categoryName) const -// { -// for (VecComponentIterator it=components.begin(); it != components.end(); ++it) -// { -// if ((*it)->getName().find(categoryName) != std::string::npos) -// return *it; -// } -// return nullptr; -// } -// -// -// std::vector CategoryLibrary::getCategories() -// { -// return std::vector{ -// "AnimationLoop", -// "BehaviorModel", -// "CollisionAlgorithm", -// "CollisionModel", -// "ConfigurationSetting", -// "ConstraintSet", -// "ConstraintSolver", -// "ConstraintSolver", -// "ContextObject", -// "Controller", -// "Engine", -// "Exporter", -// "ForceField", -// "InteractionForceField", -// "LinearSolver", -// "LinearSystem", -// "Loader", -// "Mapping", -// "Mass", -// "MechanicalState", -// "OdeSolver", -// "OrderingMethod", -// "ProjectiveConstraintSet", -// "TopologicalMapping", -// "Topology", -// "TopologyObject", -// "VisualModel", -// "_Miscellaneous" -// }; -// } -// -// void CategoryLibrary::getCategories(const objectmodel::BaseClass* mclass, -// std::vector& v) -// { -// if (mclass->hasParent(objectmodel::ContextObject::GetClass())) -// v.push_back("ContextObject"); -// if (mclass->hasParent(visual::VisualModel::GetClass())) -// v.push_back("VisualModel"); -// if (mclass->hasParent(BehaviorModel::GetClass())) -// v.push_back("BehaviorModel"); -// if (mclass->hasParent(CollisionModel::GetClass())) -// v.push_back("CollisionModel"); -// if (mclass->hasParent(behavior::BaseMechanicalState::GetClass())) -// v.push_back("MechanicalState"); -// // A Mass is a technically a ForceField, but we don't want it to appear in the ForceField category -// if (mclass->hasParent(behavior::BaseForceField::GetClass()) && !mclass->hasParent(behavior::BaseMass::GetClass())) -// v.push_back("ForceField"); -// if (mclass->hasParent(behavior::BaseInteractionForceField::GetClass())) -// v.push_back("InteractionForceField"); -// if (mclass->hasParent(behavior::BaseProjectiveConstraintSet::GetClass())) -// v.push_back("ProjectiveConstraintSet"); -// if (mclass->hasParent(behavior::BaseConstraintSet::GetClass())) -// v.push_back("ConstraintSet"); -// if (mclass->hasParent(BaseMapping::GetClass())) -// v.push_back("Mapping"); -// if (mclass->hasParent(DataEngine::GetClass())) -// v.push_back("Engine"); -// if (mclass->hasParent("BaseSimulationExporter")) -// v.push_back("Exporter"); -// if (mclass->hasParent(topology::TopologicalMapping::GetClass())) -// v.push_back("TopologicalMapping"); -// if (mclass->hasParent(behavior::BaseMass::GetClass())) -// v.push_back("Mass"); -// if (mclass->hasParent(behavior::OdeSolver::GetClass())) -// v.push_back("OdeSolver"); -// if (mclass->hasParent(behavior::ConstraintSolver::GetClass())) -// v.push_back("ConstraintSolver"); -// if (mclass->hasParent(behavior::BaseConstraintCorrection::GetClass())) -// v.push_back("ConstraintSolver"); -// if (mclass->hasParent(behavior::BaseMatrixLinearSystem::GetClass())) -// v.push_back("LinearSystem"); -// if (mclass->hasParent(behavior::BaseOrderingMethod::GetClass())) -// v.push_back("OrderingMethod"); -// if (mclass->hasParent(behavior::LinearSolver::GetClass())) -// v.push_back("LinearSolver"); -// if (mclass->hasParent(behavior::BaseAnimationLoop::GetClass())) -// v.push_back("AnimationLoop"); -// // Just like Mass and ForceField, we don't want TopologyObject to appear in the Topology category -// if (mclass->hasParent(topology::Topology::GetClass()) && !mclass->hasParent(topology::BaseTopologyObject::GetClass())) -// v.push_back("Topology"); -// if (mclass->hasParent(topology::BaseTopologyObject::GetClass())) -// v.push_back("TopologyObject"); -// if (mclass->hasParent(behavior::BaseController::GetClass())) -// v.push_back("Controller"); -// if (mclass->hasParent(loader::BaseLoader::GetClass())) -// v.push_back("Loader"); -// if (mclass->hasParent(collision::CollisionAlgorithm::GetClass())) -// v.push_back("CollisionAlgorithm"); -// if (mclass->hasParent(collision::Pipeline::GetClass())) -// v.push_back("CollisionAlgorithm"); -// if (mclass->hasParent(collision::Intersection::GetClass())) -// v.push_back("CollisionAlgorithm"); -// if (mclass->hasParent(objectmodel::ConfigurationSetting::GetClass())) -// v.push_back("ConfigurationSetting"); -// if (v.empty()) -// v.push_back("_Miscellaneous"); -// } -// -// -// } // namespace sofa::core +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sofa::core +{ + +std::vector CategoryLibrary::getCategories() +{ + return std::vector{ + "AnimationLoop", + "BehaviorModel", + "CollisionAlgorithm", + "CollisionModel", + "ConfigurationSetting", + "ConstraintSet", + "ConstraintSolver", + "ConstraintSolver", + "ContextObject", + "Controller", + "Engine", + "Exporter", + "ForceField", + "InteractionForceField", + "LinearSolver", + "LinearSystem", + "Loader", + "Mapping", + "Mass", + "MechanicalState", + "OdeSolver", + "OrderingMethod", + "ProjectiveConstraintSet", + "TopologicalMapping", + "Topology", + "TopologyObject", + "VisualModel", + "_Miscellaneous" + }; +} + +void CategoryLibrary::getCategories(const objectmodel::BaseClass* mclass, + std::vector& v) +{ + if (mclass->hasParent(objectmodel::ContextObject::GetClass())) + v.push_back("ContextObject"); + if (mclass->hasParent(visual::VisualModel::GetClass())) + v.push_back("VisualModel"); + if (mclass->hasParent(BehaviorModel::GetClass())) + v.push_back("BehaviorModel"); + if (mclass->hasParent(CollisionModel::GetClass())) + v.push_back("CollisionModel"); + if (mclass->hasParent(behavior::BaseMechanicalState::GetClass())) + v.push_back("MechanicalState"); + // A Mass is a technically a ForceField, but we don't want it to appear in the ForceField category + if (mclass->hasParent(behavior::BaseForceField::GetClass()) && !mclass->hasParent(behavior::BaseMass::GetClass())) + v.push_back("ForceField"); + if (mclass->hasParent(behavior::BaseInteractionForceField::GetClass())) + v.push_back("InteractionForceField"); + if (mclass->hasParent(behavior::BaseProjectiveConstraintSet::GetClass())) + v.push_back("ProjectiveConstraintSet"); + if (mclass->hasParent(behavior::BaseConstraintSet::GetClass())) + v.push_back("ConstraintSet"); + if (mclass->hasParent(BaseMapping::GetClass())) + v.push_back("Mapping"); + if (mclass->hasParent(DataEngine::GetClass())) + v.push_back("Engine"); + if (mclass->hasParent("BaseSimulationExporter")) + v.push_back("Exporter"); + if (mclass->hasParent(topology::TopologicalMapping::GetClass())) + v.push_back("TopologicalMapping"); + if (mclass->hasParent(behavior::BaseMass::GetClass())) + v.push_back("Mass"); + if (mclass->hasParent(behavior::OdeSolver::GetClass())) + v.push_back("OdeSolver"); + if (mclass->hasParent(behavior::ConstraintSolver::GetClass())) + v.push_back("ConstraintSolver"); + if (mclass->hasParent(behavior::BaseConstraintCorrection::GetClass())) + v.push_back("ConstraintSolver"); + if (mclass->hasParent(behavior::BaseMatrixLinearSystem::GetClass())) + v.push_back("LinearSystem"); + if (mclass->hasParent(behavior::BaseOrderingMethod::GetClass())) + v.push_back("OrderingMethod"); + if (mclass->hasParent(behavior::LinearSolver::GetClass())) + v.push_back("LinearSolver"); + if (mclass->hasParent(behavior::BaseAnimationLoop::GetClass())) + v.push_back("AnimationLoop"); + // Just like Mass and ForceField, we don't want TopologyObject to appear in the Topology category + if (mclass->hasParent(topology::Topology::GetClass()) && !mclass->hasParent(topology::BaseTopologyObject::GetClass())) + v.push_back("Topology"); + if (mclass->hasParent(topology::BaseTopologyObject::GetClass())) + v.push_back("TopologyObject"); + if (mclass->hasParent(behavior::BaseController::GetClass())) + v.push_back("Controller"); + if (mclass->hasParent(loader::BaseLoader::GetClass())) + v.push_back("Loader"); + if (mclass->hasParent(collision::CollisionAlgorithm::GetClass())) + v.push_back("CollisionAlgorithm"); + if (mclass->hasParent(collision::Pipeline::GetClass())) + v.push_back("CollisionAlgorithm"); + if (mclass->hasParent(collision::Intersection::GetClass())) + v.push_back("CollisionAlgorithm"); + if (mclass->hasParent(objectmodel::ConfigurationSetting::GetClass())) + v.push_back("ConfigurationSetting"); + if (v.empty()) + v.push_back("_Miscellaneous"); +} + + +} // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h index cce84c415cf..16ed898d803 100644 --- a/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h +++ b/Sofa/framework/Core/src/sofa/core/CategoryLibrary.h @@ -1,83 +1,71 @@ -// /****************************************************************************** -// * SOFA, Simulation Open-Framework Architecture * -// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -// * * -// * This program is free software; you can redistribute it and/or modify it * -// * under the terms of the GNU Lesser General Public License as published by * -// * the Free Software Foundation; either version 2.1 of the License, or (at * -// * your option) any later version. * -// * * -// * This program is distributed in the hope that it will be useful, but WITHOUT * -// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -// * for more details. * -// * * -// * You should have received a copy of the GNU Lesser General Public License * -// * along with this program. If not, see . * -// ******************************************************************************* -// * Authors: The SOFA Team and external contributors (see Authors.txt) * -// * * -// * Contact information: contact@sofa-framework.org * -// ******************************************************************************/ -// #pragma once -// -// #include -// -// namespace sofa::core -// { -// -// -// using Creator = sofa::core::ObjectFactory::BaseObjectCreator; -// -// /** -// * \brief An Generic Category of the Sofa Library -// * -// * It contains all the components available for Sofa corresponding to a given category (force field, mass, mapping...) -// * This Interface is used for the Modeler mainly. -// * -// */ -// class SOFA_CORE_API CategoryLibrary -// { -// public: -// typedef std::vector< ComponentLibrary* > VecComponent; -// typedef VecComponent::const_iterator VecComponentIterator; -// -// CategoryLibrary( const std::string &categoryName); -// virtual ~CategoryLibrary() {} -// -// virtual ComponentLibrary *addComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles); -// virtual void endConstruction(); -// -// const std::string &getName() const { return name;} -// const VecComponent &getComponents() const {return components;} -// -// const ComponentLibrary *getComponent( const std::string &componentName) const; -// -// size_t getNumComponents() const {return components.size();} -// -// /** \brief Get the list of categories a class belongs to, based on its parent classes. -// * -// * @param mclass the class the categorize -// * @param outputVector the vector where to push the categories -// * -// * The categories are: AnimationLoop, BehaviorModel, -// * CollisionAlgorithm, CollisionAlgorithm, CollisionAlgorithm, -// * CollisionModel, ConfigurationSetting, ConstraintSet, -// * ConstraintSolver, ConstraintSolver, ContextObject, Controller, -// * Engine, Exporter, ForceField, InteractionForceField, LinearSolver, LinearSystem, -// * Loader, Mapping, Mass, MechanicalState, OdeSolver, OrderingMethod, -// * ProjectiveConstraintSet, TopologicalMapping, Topology, -// * TopologyObject, and VisualModel -// */ -// static void getCategories(const sofa::core::objectmodel::BaseClass* mclass, -// std::vector& outputVector); -// static std::vector getCategories(); -// -// protected: -// virtual ComponentLibrary *createComponent(const std::string &componentName, ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) {return new ComponentLibrary(componentName, name, entry, exampleFiles);} -// -// std::string name; -// VecComponent components; -// }; -// -// } // namespace sofa::core +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include + +namespace sofa::core +{ + +class SOFA_CORE_API CategoryLibrary +{ +public: + CategoryLibrary( const std::string &categoryName) = delete; + virtual ~CategoryLibrary() {} + + virtual ComponentLibrary *addComponent(const std::string &componentName, ComponentFactory::ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) = delete; + virtual void endConstruction() = delete; + + const std::string &getName() const = delete; + const std::vector< ComponentLibrary* > &getComponents() const = delete; + + const ComponentLibrary *getComponent( const std::string &componentName) const = delete; + + size_t getNumComponents() const = delete; + + /** \brief Get the list of categories a class belongs to, based on its parent classes. + * + * @param mclass the class the categorize + * @param outputVector the vector where to push the categories + * + * The categories are: AnimationLoop, BehaviorModel, + * CollisionAlgorithm, CollisionAlgorithm, CollisionAlgorithm, + * CollisionModel, ConfigurationSetting, ConstraintSet, + * ConstraintSolver, ConstraintSolver, ContextObject, Controller, + * Engine, Exporter, ForceField, InteractionForceField, LinearSolver, LinearSystem, + * Loader, Mapping, Mass, MechanicalState, OdeSolver, OrderingMethod, + * ProjectiveConstraintSet, TopologicalMapping, Topology, + * TopologyObject, and VisualModel + */ + static void getCategories(const sofa::core::objectmodel::BaseClass* mclass, + std::vector& outputVector); + static std::vector getCategories(); + +protected: + virtual ComponentLibrary *createComponent(const std::string &componentName, ComponentFactory::ClassEntry::SPtr entry, const std::vector< std::string > &exampleFiles) = delete; + + DeprecatedAndRemoved name; + DeprecatedAndRemoved components; +}; + +} // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp index 265889f3919..694385b181c 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.cpp @@ -1,110 +1,28 @@ -// /****************************************************************************** -// * SOFA, Simulation Open-Framework Architecture * -// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -// * * -// * This program is free software; you can redistribute it and/or modify it * -// * under the terms of the GNU Lesser General Public License as published by * -// * the Free Software Foundation; either version 2.1 of the License, or (at * -// * your option) any later version. * -// * * -// * This program is distributed in the hope that it will be useful, but WITHOUT * -// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -// * for more details. * -// * * -// * You should have received a copy of the GNU Lesser General Public License * -// * along with this program. If not, see . * -// ******************************************************************************* -// * Authors: The SOFA Team and external contributors (see Authors.txt) * -// * * -// * Contact information: contact@sofa-framework.org * -// ******************************************************************************/ -// -// #include -// #include -// -// namespace sofa::core -// { -// -// std::string caseInsensitive(const std::string &text) -// { -// std::string result; result.resize(text.size()); -// for (unsigned int i=0; i(toupper(text[i])); -// return result; -// } -// -// //------------------------------------------------------------------------------------------------------- -// ComponentLibrary::ComponentLibrary( const std::string &componentN, const std::string &categoryN, ClassEntry::SPtr e, const std::vector< std::string > &exampleFiles): name(componentN), categoryName(categoryN),entry(e) -// { -// -// description = std::string("

") + entry->className + std::string(": "); -// -// std::vector< std::string > possiblePaths; -// -// std::vector categories; -// const objectmodel::BaseClass* entryClass = entry->creatorMap.begin()->second->getClass(); -// CategoryLibrary::getCategories(entryClass, categories); -// for (std::vector< std::string >::iterator it=categories.begin(); it!=categories.end() ; ++it) -// { -// if (it != categories.begin()) description += std::string(", "); -// description += (*it); -// } -// -// //Find a scene -// std::string nameComponentCaseInsensitive = caseInsensitive(entry->className); -// -// for (unsigned int i=0; iclassName.c_str()) >= 0 ) -// if (exampleCaseInsensitive.find(nameComponentCaseInsensitive) != std::string::npos) -// possiblePaths.push_back(exampleFiles[i]); -// } -// -// std::string nameSpace = sofa::helper::NameDecoder::decodeNamespaceName(entry->creatorMap.begin()->second->type()); -// -// description += std::string("

"); -// -// description += std::string("
    "); -// -// description += std::string("
  • Description: ") + entry->description + std::string("
  • "); -// -// -// if (!nameSpace.empty()) -// description += std::string("
  • NameSpace: ")+nameSpace +std::string("
  • "); -// if (!entry->authors.empty()) -// description += std::string("
  • Authors: ")+entry->authors +std::string("
  • "); -// if (!entry->license.empty()) -// description += std::string("
  • License: ") + entry->license + std::string("
  • "); -// if (!entry->documentationURL.empty()) -// description += std::string("
  • Documentation: ") + entry->documentationURL + std::string("
  • "); -// -// if (possiblePaths.size() != 0) -// { -// description += std::string("
  • Example: "); -// } -// -// description += std::string("
"); -// } -// -// -// -// -// -// void ComponentLibrary::addTemplate( const std::string &nameT) -// { -// if (nameT.empty()) return; -// templateName.push_back(nameT); -// } -// -// -// void ComponentLibrary::endConstruction() -// { -// } -// -// } +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include + +namespace sofa::core +{ + +} diff --git a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h index b37751d5426..7d440547b61 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentLibrary.h @@ -1,63 +1,31 @@ -// /****************************************************************************** -// * SOFA, Simulation Open-Framework Architecture * -// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -// * * -// * This program is free software; you can redistribute it and/or modify it * -// * under the terms of the GNU Lesser General Public License as published by * -// * the Free Software Foundation; either version 2.1 of the License, or (at * -// * your option) any later version. * -// * * -// * This program is distributed in the hope that it will be useful, but WITHOUT * -// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -// * for more details. * -// * * -// * You should have received a copy of the GNU Lesser General Public License * -// * along with this program. If not, see . * -// ******************************************************************************* -// * Authors: The SOFA Team and external contributors (see Authors.txt) * -// * * -// * Contact information: contact@sofa-framework.org * -// ******************************************************************************/ -// #pragma once -// -// #include -// -// namespace sofa::core -// { -// -// typedef sofa::core::ObjectFactory::ClassEntry ClassEntry; -// -// /** -// * \brief An Generic Component of the Sofa Library -// * -// * It contains all the information related to a Sofa component: its name, the templates available, a description of it, its creator, ... -// * This Interface is used for the Modeler mainly. -// * -// */ -// class SOFA_CORE_API ComponentLibrary -// { -// public: -// ComponentLibrary(const std::string& componentName, const std::string& categoryName, ClassEntry::SPtr entry, const std::vector< std::string >& exampleFiles); -// virtual ~ComponentLibrary() {} -// -// virtual void addTemplate( const std::string& templateName); -// virtual void endConstruction(); -// virtual void setDisplayed(bool ) {} -// -// const std::string& getName() const { return name;} -// const std::string& getDescription() const { return description;} -// const std::string& getCategory() const { return categoryName;} -// const std::vector< std::string >& getTemplates() const { return templateName;} -// const ClassEntry::SPtr getEntry() const { return entry;} -// -// protected: -// //-------------------------------------------- -// //Sofa information -// std::string name; -// std::vector< std::string > templateName; -// std::string description; -// std::string categoryName; -// ClassEntry::SPtr entry; -// }; -// } +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include + +namespace sofa::core +{ + +using ComponentLibrary = DeprecatedAndRemoved; + +} From 134cbb4f7c5ab0b19b38b3e29eb2c059d4a162bd Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 14:31:43 +0200 Subject: [PATCH 48/71] fix old registration (before the legacy) --- Sofa/Component/src/sofa/component/init.cpp | 6 +++--- .../Core/src/sofa/core/ComponentFactory.h | 15 ++++++++++++++ .../src/sofa/core/ComponentRegistrationData.h | 1 - .../FailingPlugin/ComponentFailingPlugin.cpp | 11 +++++++--- .../Helper/test/system/FailingPlugin/init.cpp | 11 ++++++++++ .../Helper/test/system/PluginManager_test.cpp | 13 ------------ .../test/system/TestPluginA/ComponentA.cpp | 13 ++++++++---- .../test/system/TestPluginA/ComponentB.cpp | 20 ++++++++++++++----- .../system/TestPluginA/initTestPluginA.cpp | 13 ++++++++++++ 9 files changed, 74 insertions(+), 29 deletions(-) diff --git a/Sofa/Component/src/sofa/component/init.cpp b/Sofa/Component/src/sofa/component/init.cpp index 6e1494deb84..3bb59ff5a99 100644 --- a/Sofa/Component/src/sofa/component/init.cpp +++ b/Sofa/Component/src/sofa/component/init.cpp @@ -44,7 +44,7 @@ #include #include -#include +#include #include namespace sofa::component @@ -54,7 +54,7 @@ extern "C" { SOFA_EXPORT_DYNAMIC_LIBRARY void initExternalModule(); SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleName(); SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleVersion(); - SOFA_EXPORT_DYNAMIC_LIBRARY void registerObjects(sofa::core::ObjectFactory * factory); + SOFA_EXPORT_DYNAMIC_LIBRARY void registerObjects(sofa::core::ComponentFactory * factory); } void initExternalModule() @@ -72,7 +72,7 @@ const char* getModuleVersion() return MODULE_VERSION; } -void registerObjects(sofa::core::ObjectFactory* factory) +void registerObjects(sofa::core::ComponentFactory* factory) { factory->registerObjectsFromPlugin(Sofa.Component.AnimationLoop); factory->registerObjectsFromPlugin(Sofa.Component.Collision); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 30ca9d5b962..6437bff9ff9 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -21,6 +21,7 @@ ******************************************************************************/ #pragma once +#include #include #include #include @@ -74,6 +75,15 @@ class SOFA_CORE_API ComponentFactory void getEntry(std::string) = delete; + void getAllEntries(std::vector& result, bool filterUnloadedPlugins = true) = delete; + + void dumpXML(std::ostream& out = std::cout) = delete; + void dumpHTML(std::ostream& out = std::cout) = delete; + + static std::string ShortName(std::string classname) = delete; + static void ResetAlias(std::string name, ClassEntry::SPtr previous) = delete; + static bool AddAlias(std::string name, std::string result, bool force=false, + ClassEntry::SPtr* previous = nullptr) = delete; protected: @@ -146,4 +156,9 @@ struct SOFA_CORE_API MainComponentFactory }; +template +using ObjectCreator = DeprecatedAndRemoved; + +using RegisterObject = DeprecatedAndRemoved; + } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 461cc741175..18fe55a31fc 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -43,7 +43,6 @@ struct SOFA_CORE_API ComponentRegistrationData std::vector> templateAttributes; - std::string componentNamespace; std::string componentModule; unsigned int instantiationPriority {}; diff --git a/Sofa/framework/Helper/test/system/FailingPlugin/ComponentFailingPlugin.cpp b/Sofa/framework/Helper/test/system/FailingPlugin/ComponentFailingPlugin.cpp index f85cd1910c7..a5fdb1f2a50 100644 --- a/Sofa/framework/Helper/test/system/FailingPlugin/ComponentFailingPlugin.cpp +++ b/Sofa/framework/Helper/test/system/FailingPlugin/ComponentFailingPlugin.cpp @@ -36,9 +36,14 @@ ComponentFailingPlugin::~ComponentFailingPlugin() { } - -int ComponentFailingPluginClass = core::RegisterObject("ComponentFailingPlugin").add< ComponentFailingPlugin >(); - +void registerComponent(sofa::core::ObjectFactory* factory) +{ + factory->registerComponent( + core::CreateComponent("ComponentFailingPlugin") + .withModule("FailingPlugin") + .withDescription("dummy component") + ); +} } // namespace sofa::test diff --git a/Sofa/framework/Helper/test/system/FailingPlugin/init.cpp b/Sofa/framework/Helper/test/system/FailingPlugin/init.cpp index 96fbe8751e6..f7d1c80b06d 100644 --- a/Sofa/framework/Helper/test/system/FailingPlugin/init.cpp +++ b/Sofa/framework/Helper/test/system/FailingPlugin/init.cpp @@ -20,6 +20,12 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #include +#include + +namespace sofa::test +{ +extern void registerComponent(sofa::core::ComponentFactory* factory); +} extern "C" { @@ -67,6 +73,11 @@ SOFA_FAILINGPLUGIN_API const char* getModuleComponentList() return "ComponentFailingPlugin"; } +void registerObjects(sofa::core::ComponentFactory* factory) +{ + sofa::test::registerComponent(factory); +} + } // extern "C" namespace failingplugin diff --git a/Sofa/framework/Helper/test/system/PluginManager_test.cpp b/Sofa/framework/Helper/test/system/PluginManager_test.cpp index 453c9ccb243..1a255758cd0 100644 --- a/Sofa/framework/Helper/test/system/PluginManager_test.cpp +++ b/Sofa/framework/Helper/test/system/PluginManager_test.cpp @@ -331,17 +331,4 @@ TEST_F(PluginManager_test, failingPlugin) "registered the object has been unloaded, preventing the object creation.") != std::string::npos; }), description.getErrors().end()); - - std::vector entries; - sofa::core::ObjectFactory::getInstance()->getAllEntries(entries, false); - EXPECT_NE( - std::find_if(entries.begin(), entries.end(), [](const auto& entry){ return entry->className == "ComponentFailingPlugin";}), - entries.end() - ); - - sofa::core::ObjectFactory::getInstance()->getAllEntries(entries, true); - EXPECT_EQ( - std::find_if(entries.begin(), entries.end(), [](const auto& entry){ return entry->className == "ComponentFailingPlugin";}), - entries.end() - ); } diff --git a/Sofa/framework/Helper/test/system/TestPluginA/ComponentA.cpp b/Sofa/framework/Helper/test/system/TestPluginA/ComponentA.cpp index 091eb502526..1c7a80f7021 100644 --- a/Sofa/framework/Helper/test/system/TestPluginA/ComponentA.cpp +++ b/Sofa/framework/Helper/test/system/TestPluginA/ComponentA.cpp @@ -21,7 +21,7 @@ ******************************************************************************/ #include "ComponentA.h" -#include +#include namespace sofa::test @@ -36,9 +36,14 @@ ComponentA::~ComponentA() { } - -int ComponentAClass = core::RegisterObject("Component A").add< ComponentA >(); - +void registerComponentA(sofa::core::ComponentFactory* factory) +{ + factory->registerComponent( + core::CreateComponent("ComponentA") + .withModule("TestPluginA") + .withDescription("dummy component") + ); +} } // namespace sofa::test diff --git a/Sofa/framework/Helper/test/system/TestPluginA/ComponentB.cpp b/Sofa/framework/Helper/test/system/TestPluginA/ComponentB.cpp index 18573f62896..35656f6f837 100644 --- a/Sofa/framework/Helper/test/system/TestPluginA/ComponentB.cpp +++ b/Sofa/framework/Helper/test/system/TestPluginA/ComponentB.cpp @@ -41,12 +41,22 @@ ComponentB::~ComponentB() { } -int ComponentBClass = sofa::core::RegisterObject("Component B") - .add< ComponentB >() - .add< ComponentB >() - .add< ComponentB >() +template +void registerComponent(sofa::core::ComponentFactory* factory) +{ + factory->registerComponent( + core::CreateComponent>("ComponentB") + .withModule("TestPluginA") + .withDescription("dummy component") + ); +} -; +void registerComponentB(sofa::core::ComponentFactory* factory) +{ + registerComponent>(factory); + registerComponent>(factory); + registerComponent>(factory); +} template class SOFA_TESTPLUGINA_API ComponentB; template class SOFA_TESTPLUGINA_API ComponentB; diff --git a/Sofa/framework/Helper/test/system/TestPluginA/initTestPluginA.cpp b/Sofa/framework/Helper/test/system/TestPluginA/initTestPluginA.cpp index cac7864f0ba..89659de8147 100644 --- a/Sofa/framework/Helper/test/system/TestPluginA/initTestPluginA.cpp +++ b/Sofa/framework/Helper/test/system/TestPluginA/initTestPluginA.cpp @@ -20,6 +20,13 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #include +#include + +namespace sofa::test +{ +extern void registerComponentA(sofa::core::ComponentFactory* factory); +extern void registerComponentB(sofa::core::ComponentFactory* factory); +} extern "C" { @@ -61,6 +68,12 @@ SOFA_TESTPLUGINA_API const char* getModuleComponentList() return "ComponentA, ComponentB"; } +void registerObjects(sofa::core::ComponentFactory* factory) +{ + sofa::test::registerComponentA(factory); + sofa::test::registerComponentB(factory); +} + } // extern "C" namespace testplugina From 42e6e9d6569cd3a868e60d380d622f0bdc6b480a Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 14:31:50 +0200 Subject: [PATCH 49/71] change json --- .../Core/src/sofa/core/ObjectFactoryJson.cpp | 273 ++++++++---------- .../Core/src/sofa/core/ObjectFactoryJson.h | 10 +- 2 files changed, 121 insertions(+), 162 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp index cb623e830f3..e525ae3d752 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.cpp @@ -1,157 +1,116 @@ -// /****************************************************************************** -// * SOFA, Simulation Open-Framework Architecture * -// * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -// * * -// * This program is free software; you can redistribute it and/or modify it * -// * under the terms of the GNU Lesser General Public License as published by * -// * the Free Software Foundation; either version 2.1 of the License, or (at * -// * your option) any later version. * -// * * -// * This program is distributed in the hope that it will be useful, but WITHOUT * -// * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -// * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -// * for more details. * -// * * -// * You should have received a copy of the GNU Lesser General Public License * -// * along with this program. If not, see . * -// ******************************************************************************* -// * Authors: The SOFA Team and external contributors (see Authors.txt) * -// * * -// * Contact information: contact@sofa-framework.org * -// ******************************************************************************/ -// #include -// #include -// #include -// #include -// -// -// namespace sofa::core -// { -// -// namespace objectmodel -// { -// -// inline void to_json(nlohmann::json& json, -// const objectmodel::BaseClass& baseClass) -// { -// json["namespaceName"] = baseClass.namespaceName; -// json["typeName"] = baseClass.typeName; -// json["className"] = baseClass.className; -// json["templateName"] = baseClass.templateName; -// json["shortName"] = baseClass.shortName; -// -// sofa::type::vector parents; -// parents.reserve(baseClass.parents.size()); -// for (const auto* parent : baseClass.parents) -// { -// if (parent) -// { -// parents.push_back(parent->typeName); -// } -// } -// json["parents"] = parents; -// -// std::vector categories; -// sofa::core::CategoryLibrary::getCategories(&baseClass, categories); -// json["categories"] = categories; -// } -// -// inline void to_json(nlohmann::json& json, -// const objectmodel::BaseData* data) -// { -// if (data) -// { -// json["name"] = data->m_name; -// json["group"] = data->group; -// json["help"] = data->help; -// json["type"] = data->getValueTypeString(); -// json["defaultValue"] = data->getDefaultValueString(); -// } -// } -// -// inline void to_json(nlohmann::json& json, -// const objectmodel::BaseLink* link) -// { -// if (link) -// { -// json["name"] = link->getName(); -// json["help"] = link->getHelp(); -// json["destinationTypeName"] = link->getValueTypeString(); -// } -// } -// -// inline void to_json(nlohmann::json& json, -// const objectmodel::BaseComponent::SPtr& object) -// { -// if (object) -// { -// json["data"] = object->getDataFields(); -// json["link"] = object->getLinks(); -// } -// } -// -// } -// -// inline void to_json(nlohmann::json& json, -// const sofa::core::ObjectFactory::BaseObjectCreator::SPtr& creator) -// { -// if (creator) -// { -// if (const char* target = creator->getTarget()) -// { -// json["target"] = target; -// } -// else -// { -// json["target"] = "targetCannotBeFound"; -// } -// json["class"] = *creator->getClass(); -// -// sofa::core::objectmodel::BaseObjectDescription desc; -// if (const auto object = creator->createInstance(nullptr, &desc)) -// { -// json["object"] = object; -// } -// } -// } -// -// inline void to_json(nlohmann::json& json, -// const sofa::core::ObjectFactory::ClassEntry::SPtr& entry) -// { -// if (entry) -// { -// json["className"] = entry->className; -// json["description"] = entry->description; -// -// json["creator"] = entry->creatorMap; -// } -// } -// -// std::string ObjectFactoryJson::dump(ObjectFactory* factory) -// { -// if (!factory) -// { -// msg_error("ObjectFactoryJson") << "Invalid factory: cannot dump to json"; -// return {}; -// } -// -// std::vector entries; -// factory->getAllEntries(entries, true); -// -// const nlohmann::json json = entries; -// -// std::string dump{}; -// -// try -// { -// dump = json.dump(); -// } -// catch (const nlohmann::json::type_error& e) -// { -// msg_error("ObjectFactoryJson") << "Error while dumping json from the object factory: " << e.what(); -// dump = json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); -// } -// -// return dump; -// } -// } +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include +#include + + +namespace sofa::core +{ + +namespace objectmodel +{ + +inline void to_json(nlohmann::json& json, + const objectmodel::BaseData* data) +{ + if (data) + { + json["name"] = data->m_name; + json["group"] = data->group; + json["help"] = data->help; + json["type"] = data->getValueTypeString(); + json["defaultValue"] = data->getDefaultValueString(); + } +} + +inline void to_json(nlohmann::json& json, + const objectmodel::BaseLink* link) +{ + if (link) + { + json["name"] = link->getName(); + json["help"] = link->getHelp(); + json["destinationTypeName"] = link->getValueTypeString(); + } +} + +inline void to_json(nlohmann::json& json, + const objectmodel::BaseComponent::SPtr& object) +{ + if (object) + { + json["data"] = object->getDataFields(); + json["link"] = object->getLinks(); + } +} + +} + +inline void to_json(nlohmann::json& json, + const sofa::core::ComponentRegistrationData::SPtr& entry) +{ + if (entry) + { + json["name"] = entry->componentName; + json["attributes"] = entry->templateAttributes; + json["module"] = entry->componentModule; + json["description"] = entry->description; + json["documentationURL"] = entry->documentationURL; + // json["templateDeductionRule"] = entry->templateDeductionRule; + + if (entry->creator) + { + auto component = entry->creator->create(); + json["component"] = component; + } + } +} + +std::string ObjectFactoryJson::dump(ComponentFactory* factory) +{ + if (!factory) + { + msg_error("ObjectFactoryJson") << "Invalid factory: cannot dump to json"; + return {}; + } + + const auto& registry = factory->getRegistry(); + + const nlohmann::json json = registry; + + std::string dump{}; + + try + { + dump = json.dump(); + } + catch (const nlohmann::json::type_error& e) + { + msg_error("ObjectFactoryJson") << "Error while dumping json from the object factory: " << e.what(); + dump = json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); + } + + return dump; +} +} diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h index 9fd4cba4e97..84fc7c211f6 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactoryJson.h @@ -26,10 +26,10 @@ namespace sofa::core { -// class ObjectFactory; -// struct SOFA_CORE_API ObjectFactoryJson -// { -// static std::string dump(ObjectFactory* factory); -// }; +class ComponentFactory; +struct SOFA_CORE_API ObjectFactoryJson +{ + static std::string dump(ComponentFactory* factory); +}; } From 082b036afdd901d72faccd73b20330ad9f79e9ed Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 15:15:24 +0200 Subject: [PATCH 50/71] try to fix --- Sofa/framework/Core/src/sofa/core/ComponentFactory.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 6437bff9ff9..1c5b47860ef 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -63,14 +63,13 @@ class SOFA_CORE_API ComponentFactory std::string listClassesDerivedFrom(const std::string& separator = ", ") const; - SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() - objectmodel::BaseComponent::SPtr createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + objectmodel::BaseComponent::SPtr SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); SOFA_CORE_DEPRECATED_OBJECTFACTORY_REGISTEROBJECTS() bool registerObjects(LegacyComponentRegistrationData& ro); - static SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::getInstance instead") ComponentFactory* getInstance(); - static SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::CreateComponent instead") objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + static ComponentFactory* SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::getInstance instead") getInstance(); + static objectmodel::BaseComponent::SPtr SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::CreateComponent instead") CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); static bool SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::HasCreator instead") HasCreator(const std::string& classname); From 9711eff70d00c99a0c2dfa88ee35dc483ed8fce0 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 15:22:29 +0200 Subject: [PATCH 51/71] try again --- Sofa/framework/Core/src/sofa/core/ComponentFactory.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 1c5b47860ef..638e1419a8a 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -68,9 +68,14 @@ class SOFA_CORE_API ComponentFactory SOFA_CORE_DEPRECATED_OBJECTFACTORY_REGISTEROBJECTS() bool registerObjects(LegacyComponentRegistrationData& ro); - static ComponentFactory* SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::getInstance instead") getInstance(); - static objectmodel::BaseComponent::SPtr SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::CreateComponent instead") CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - static bool SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::HasCreator instead") HasCreator(const std::string& classname); + SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::getInstance instead") + static ComponentFactory* getInstance(); + + SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::CreateComponent instead") + static objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + + SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::HasCreator instead") + static bool HasCreator(const std::string& classname); void getEntry(std::string) = delete; From a4d7ae7aaf2ae9876c71f4bd6f0c71ce3d4f8d26 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 15:56:39 +0200 Subject: [PATCH 52/71] separation of concerns --- .../Core/src/sofa/core/ComponentCreator.h | 10 -------- .../Core/src/sofa/core/ComponentFactory.h | 2 +- .../src/sofa/core/ComponentRegistrationData.h | 25 +++++++++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h index 663e876f587..9cca115d0bb 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h @@ -45,11 +45,6 @@ struct SOFA_CORE_API BaseComponentCreator */ virtual objectmodel::BaseComponent::SPtr create() const = 0; - /** - * @brief Returns the SOFA class metadata associated with the component. - */ - virtual const objectmodel::BaseClass* getClass() = 0; - /** * @brief Creates a copy of the creator. * Ensures the factory registry owns unique instances of creators during registration. @@ -75,11 +70,6 @@ struct ComponentCreator : public BaseComponentCreator return sofa::core::objectmodel::New(); } - const objectmodel::BaseClass* getClass() override - { - return RealComponent::GetClass(); - } - std::unique_ptr clone() const override { return std::make_unique(); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 638e1419a8a..ff5fcac1aa5 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -112,7 +112,7 @@ std::vector ComponentFactory::getEntriesDerived for (const auto& component : m_registry) { - if (auto* componentClassInRegistry = component->creator->getClass()) + if (auto* componentClassInRegistry = component->classData) { if (componentClassInRegistry->hasParent(componentClass)) { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 18fe55a31fc..9997ce44e68 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -55,6 +55,7 @@ struct SOFA_CORE_API ComponentRegistrationData std::shared_ptr templateDeductionRule; std::unique_ptr creator; + const BaseClass* classData { nullptr }; private: // ComponentRegistrationData() = default; @@ -64,13 +65,19 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder { ComponentRegistrationData::SPtr data; - ComponentRegistrationDataBuilder(const std::string& componentName, const std::string& moduleName, const std::string& description, std::unique_ptr creator) + ComponentRegistrationDataBuilder( + const std::string& componentName, + const std::string& moduleName, + const std::string& description, + std::unique_ptr creator, + const BaseClass* classData) : data(std::make_shared()) { data->componentName = componentName; data->componentModule = moduleName; data->description = description; data->creator = std::move(creator); + data->classData = classData; } ComponentRegistrationDataBuilder& addAlias(const std::string& alias) @@ -149,36 +156,40 @@ struct SOFA_CORE_API ComponentRegistrationDataModule { ComponentRegistrationDataBuilder withDescription(const std::string& description) { - return {m_componentName, m_moduleName, description, std::move(m_creator)}; + return {m_componentName, m_moduleName, description, std::move(m_creator), m_classData}; } - ComponentRegistrationDataModule(const std::string& componentName, const std::string& moduleName, std::unique_ptr creator) - : m_componentName(componentName), m_moduleName(moduleName), m_creator(std::move(creator)) {} + ComponentRegistrationDataModule(const std::string& componentName, const std::string& moduleName, std::unique_ptr creator, const BaseClass* classData) + : m_componentName(componentName), m_moduleName(moduleName), m_creator(std::move(creator)), m_classData(classData) {} private: std::string m_componentName; std::string m_moduleName; std::unique_ptr m_creator; + const BaseClass* m_classData; }; struct SOFA_CORE_API ComponentRegistrationDataName { ComponentRegistrationDataModule withModule(const std::string& moduleName) { - return {m_componentName, moduleName, std::move(m_creator)}; + return {m_componentName, moduleName, std::move(m_creator), m_classData}; } - ComponentRegistrationDataName(const std::string& componentName, std::unique_ptr creator) + ComponentRegistrationDataName(const std::string& componentName, std::unique_ptr creator, const BaseClass* classData) : m_componentName(componentName) , m_creator(std::move(creator)) + , m_classData(classData) {} private: std::string m_componentName; std::unique_ptr m_creator; + const BaseClass* m_classData; }; template ComponentRegistrationDataName CreateComponent(const std::string& componentName) { std::unique_ptr creator = std::make_unique>(); - return ComponentRegistrationDataName(componentName, std::move(creator)); + auto* classData = Component::GetClass(); + return ComponentRegistrationDataName(componentName, std::move(creator), classData); } From dd5db2491ec9341cd44ccb4fd0a3c24ca38d6f26 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 15:56:52 +0200 Subject: [PATCH 53/71] use 'template' keyword to treat 'addTemplateAttribute' as a dependent template name --- .../src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp index 4b80f5ec045..f5c3c94b03f 100644 --- a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp +++ b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp @@ -38,7 +38,7 @@ void registerComponent(sofa::core::ComponentFactory* factory) core::CreateComponent>("SparseLDLSolver") .withModule(MODULE_NAME) .withDescription(std::string(description)) - .addTemplateAttribute("matrixType") + .template addTemplateAttribute("matrixType") ); }; From 084a2342e4b1e38f77be3e7f4596b6a127131522 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 16:21:43 +0200 Subject: [PATCH 54/71] fix MultiThreading --- .../src/MultiThreading/DataExchange.h | 42 +------------------ .../src/MultiThreading/DataExchange.inl | 12 ++---- .../SceneCheckMultithreading.cpp | 19 +-------- 3 files changed, 7 insertions(+), 66 deletions(-) diff --git a/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.h b/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.h index 6ffa39dac11..4fa396ad4b0 100644 --- a/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.h +++ b/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.h @@ -64,7 +64,7 @@ class DataExchange : public virtual objectmodel::BaseComponent protected: - DataExchange( const char* from, const char* to ); + DataExchange(); ~DataExchange() override; @@ -83,55 +83,15 @@ class DataExchange : public virtual objectmodel::BaseComponent return sofa::defaulttype::DataTypeName::name(); } - template - static typename T::SPtr create(T*, core::objectmodel::BaseContext* context, core::objectmodel::BaseObjectDescription* arg) - { - std::string fromPath; - std::string toPath; - - if(arg) - { - fromPath = arg->getAttribute(std::string("from"), "" ); - toPath = arg->getAttribute(std::string("to"), "" ); - } - - //context->findLinkDest(stout, fromPath, NULL); - //context->findLinkDest(stout, toPath, NULL); - - typename T::SPtr obj = sofa::core::objectmodel::New(fromPath.c_str(), toPath.c_str() ); - if (context) - { - context->addObject(obj); - } - if (arg) - { - obj->parse(arg); - } - return obj; - } - - Data mSource; ///< source object to copy Data mDestination; ///< destination object to copy private: - - /// source - //SingleLink< DataExchange, Data, BaseLink::FLAG_DATALINK|BaseLink::FLAG_DUPLICATE> mSourceObject; - /// dest - //SingleLink< DataExchange, Data, BaseLink::FLAG_DATALINK|BaseLink::FLAG_DUPLICATE> mDestinationObject; - - - DataTypes* mSourcePtr; DataTypes* mDestinationPtr; //VecCoord mDataCopy; - std::string fromPath; - std::string toPath; - //core::objectmodel::BaseObjectDescription* desc; - std::size_t mSizeInBytes; }; diff --git a/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.inl b/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.inl index e8501ae6626..de1081f0638 100644 --- a/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.inl +++ b/applications/plugins/MultiThreading/src/MultiThreading/DataExchange.inl @@ -27,18 +27,14 @@ namespace sofa::core { template -DataExchange::DataExchange( const char* from, const char* to ) +DataExchange::DataExchange() : BaseObject() , mSource(initData(&mSource,"from","source object to copy")) , mDestination(initData(&mDestination,"to","destination object to copy")) , mSourcePtr(nullptr) , mDestinationPtr(nullptr) - , fromPath(from) - , toPath(to) , mSizeInBytes(0) -{ - //f_listening.setValue(true); -} +{} template DataExchange::~DataExchange() = default; @@ -63,8 +59,8 @@ void DataExchange::init() mSource.beginEdit(); mDestination.beginEdit(); - parseField( std::string("from"), fromPath ); - parseField( std::string("to"), toPath ); + // parseField( std::string("from"), fromPath ); + // parseField( std::string("to"), toPath ); core::objectmodel::BaseData* tempParent = mSource.getParent(); tempParent = mDestination.getParent(); diff --git a/applications/plugins/MultiThreading/src/MultiThreading/SceneCheckMultithreading.cpp b/applications/plugins/MultiThreading/src/MultiThreading/SceneCheckMultithreading.cpp index 0cadd90f28b..3da3efae55e 100644 --- a/applications/plugins/MultiThreading/src/MultiThreading/SceneCheckMultithreading.cpp +++ b/applications/plugins/MultiThreading/src/MultiThreading/SceneCheckMultithreading.cpp @@ -68,24 +68,9 @@ void SceneCheckMultithreading::doCheckOn(sofa::simulation::Node* node) if (!parallelImplementation.empty()) { - if (sofa::core::ObjectFactory::getInstance()->hasCreator(parallelImplementation)) + if (sofa::core::MainComponentFactory::HasCreator(parallelImplementation)) { - const auto& entry = sofa::core::ObjectFactory::getInstance()->getEntry(parallelImplementation); - auto it = entry.creatorMap.find(object->getTemplateName()); - if (it != entry.creatorMap.end()) - { - std::string seq = object->getClassName(); - if (!object->getTemplateName().empty()) - { - seq += "[" + object->getTemplateName() + "]"; - } - std::string par = entry.className; - if (!it->first.empty()) - { - par += "[" + it->first + "]"; - } - m_summary.insert({seq, par }); - } + m_summary.insert({object->d_factoryName.getValue(), parallelImplementation }); } else { From fcb9bd1467275b755c90bbd081f8cc41412a6b67 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 16:37:25 +0200 Subject: [PATCH 55/71] use 'template' keyword to treat 'addTemplateAttribute' as a dependent template name --- .../src/sofa/component/statecontainer/MechanicalObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp index 6d50274f61c..dbff2488cb9 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.cpp @@ -50,7 +50,7 @@ void registerComponent(sofa::core::ComponentFactory* factory) core::CreateComponent>("MechanicalObject") .withModule(MODULE_NAME) .withDescription(std::string(description)) - .addTemplateAttribute("dofType") + .template addTemplateAttribute("dofType") ); }; From 1f03f5341d71a655f9b18d3dbeeed62817fc0f39 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 16:52:22 +0200 Subject: [PATCH 56/71] whole module --- .../fem/elastic/BeamFEMForceField.cpp | 11 ++++-- .../fem/elastic/CorotationalFEMForceField.cpp | 2 +- .../FastTetrahedralCorotationalForceField.cpp | 9 ++++- .../fem/elastic/HexahedralFEMForceField.cpp | 9 ++++- .../HexahedralFEMForceFieldAndMass.cpp | 9 ++++- .../fem/elastic/HexahedronFEMForceField.cpp | 9 ++++- .../HexahedronFEMForceFieldAndMass.cpp | 11 ++++-- .../LinearSmallStrainFEMForceField.cpp | 37 +++++++++++++------ .../fem/elastic/QuadBendingFEMForceField.cpp | 9 ++++- .../TetrahedralCorotationalFEMForceField.cpp | 9 ++++- .../fem/elastic/TetrahedronFEMForceField.cpp | 9 ++++- .../fem/elastic/TriangleFEMForceField.cpp | 9 ++++- .../TriangularAnisotropicFEMForceField.cpp | 9 ++++- .../fem/elastic/TriangularFEMForceField.cpp | 9 ++++- .../elastic/TriangularFEMForceFieldOptim.cpp | 9 ++++- 15 files changed, 119 insertions(+), 41 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp index cad84bcd07f..5d85f22fc0c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp @@ -32,8 +32,13 @@ using namespace sofa::defaulttype; void registerBeamFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Beam finite elements.") - .add< BeamFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("BeamFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Beam finite elements.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } namespace _beamfemforcefield_ @@ -43,4 +48,4 @@ namespace _beamfemforcefield_ } // namespace _beamfemforcefield_ -} // namespace sofa::component::solidmechanics::fem::elastic \ No newline at end of file +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index 059bcb00e22..dd96bf86c23 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -38,7 +38,7 @@ void registerComponent(sofa::core::ObjectFactory* factory) core::CreateComponent>("CorotationalFEMForceField") .withModule(MODULE_NAME) .withDescription(std::string(description)) - .addTemplateAttribute("dofType") + .template addTemplateAttribute("dofType") .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) .withDeductionRule>() ); diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp index b53d9746a7e..481dad11790 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp @@ -33,8 +33,13 @@ using namespace sofa::defaulttype; void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Fast Corotational Tetrahedral Mesh.") - .add< FastTetrahedralCorotationalForceField >()); + factory->registerComponent( + core::CreateComponent>("FastTetrahedralCorotationalForceField") + .withModule(MODULE_NAME) + .withDescription("Fast Corotational Tetrahedral Mesh.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FastTetrahedralCorotationalForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp index 8f1861c8d33..78d1df90907 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp @@ -32,8 +32,13 @@ using namespace sofa::defaulttype; void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Hexahedral finite elements.") - .add< HexahedralFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("HexahedralFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Hexahedral finite elements.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API HexahedralFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp index bb5d20549a4..6b7e25332ab 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp @@ -32,8 +32,13 @@ using namespace sofa::defaulttype; void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Hexahedral finite elements with mass") - .add< HexahedralFEMForceFieldAndMass >()); + factory->registerComponent( + core::CreateComponent>("HexahedralFEMForceFieldAndMass") + .withModule(MODULE_NAME) + .withDescription("Hexahedral finite elements with mass") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API HexahedralFEMForceFieldAndMass; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp index d0a796b234d..b6e0b8836dc 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp @@ -32,8 +32,13 @@ using namespace sofa::defaulttype; void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Hexahedral finite elements.") - .add< HexahedronFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("HexahedronFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Hexahedral finite elements.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API HexahedronFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp index 79e828ac592..cfdbca95630 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp @@ -31,12 +31,17 @@ using namespace sofa::defaulttype; void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Hexahedral finite elements with mass.") - .add< HexahedronFEMForceFieldAndMass >()); + factory->registerComponent( + core::CreateComponent>("HexahedronFEMForceFieldAndMass") + .withModule(MODULE_NAME) + .withDescription("Hexahedral finite elements with mass.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API HexahedronFEMForceFieldAndMass; -} // namespace sofa::component::solidmechanics::fem::elastic \ No newline at end of file +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp index 52a1eb9a6e2..7278cdbd6d9 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp @@ -29,22 +29,35 @@ namespace sofa::component::solidmechanics::fem::elastic { -void registerLinearSmallStrainFEMForceField(sofa::core::ObjectFactory* factory) +static constexpr std::string_view description { "Hooke's law assuming small strain" }; + +template +void registerComponent(sofa::core::ObjectFactory* factory) { - factory->registerObjects(sofa::core::ObjectRegistrationData("Hooke's law assuming small strain") - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() - .add< LinearSmallStrainFEMForceField >() + factory->registerComponent( + core::CreateComponent>("CorotationalFEMForceField") + .withModule(MODULE_NAME) + .withDescription(std::string(description)) + .template addTemplateAttribute("dofType") + .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) + .withDeductionRule>() ); } +void registerLinearSmallStrainFEMForceField(sofa::core::ObjectFactory* factory) +{ + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); + registerComponent(factory); +} + template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API LinearSmallStrainFEMForceField; template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API LinearSmallStrainFEMForceField; template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API LinearSmallStrainFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp index e28740b453a..957df76be8a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp @@ -36,8 +36,13 @@ using namespace sofa::defaulttype; void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Bending Quad finite elements") - .add< QuadBendingFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("QuadBendingFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Bending Quad finite elements") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API QuadBendingFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp index 3d88496861e..8622a83bc0c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp @@ -33,8 +33,13 @@ using namespace sofa::defaulttype; void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Corotational FEM Tetrahedral finite elements") - .add< TetrahedralCorotationalFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("TetrahedralCorotationalFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Corotational FEM Tetrahedral finite elements") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API TetrahedralCorotationalFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp index 853c16850f8..62da36d5e96 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp @@ -33,8 +33,13 @@ using namespace sofa::defaulttype; void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Tetrahedral finite elements.") - .add< TetrahedronFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("TetrahedronFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Tetrahedral finite elements.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API TetrahedronFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp index 6bb56243188..4fb1d85606d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp @@ -33,8 +33,13 @@ using namespace sofa::defaulttype; void registerTriangleFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Triangular finite elements for static topology.") - .add< TriangleFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("TriangleFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Triangular finite elements for static topology.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API TriangleFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp index a0df8632e60..e6507feccc1 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp @@ -31,8 +31,13 @@ using namespace sofa::defaulttype; void registerTriangularAnisotropicFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Triangular finite element model using anisotropic material.") - .add< TriangularAnisotropicFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("TriangularAnisotropicFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Triangular finite element model using anisotropic material.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API TriangularAnisotropicFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp index 6224c5da527..125f7d795dd 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp @@ -34,8 +34,13 @@ using namespace sofa::defaulttype; void registerTriangularFEMForceField(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Corotational Triangular finite elements for dynamic topology.") - .add< TriangularFEMForceField >()); + factory->registerComponent( + core::CreateComponent>("TriangularFEMForceField") + .withModule(MODULE_NAME) + .withDescription("Corotational Triangular finite elements for dynamic topology.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API TriangularFEMForceField; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp index 5b31f4ce4d5..15badde543c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp @@ -32,8 +32,13 @@ using namespace sofa::defaulttype; void registerTriangularFEMForceFieldOptim(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Corotational Triangular finite elements.") - .add< TriangularFEMForceFieldOptim >()); + factory->registerComponent( + core::CreateComponent>("TriangularFEMForceFieldOptim") + .withModule(MODULE_NAME) + .withDescription("Corotational Triangular finite elements.") + .template addTemplateAttribute("dofType") + .withDeductionRule>() + ); } template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API TriangularFEMForceFieldOptim; From 548c010914a633b3ae8d2ad2992259ebd4535d54 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 16:53:22 +0200 Subject: [PATCH 57/71] template keyword --- .../component/solidmechanics/fem/elastic/BeamFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/CorotationalFEMForceField.cpp | 2 +- .../fem/elastic/FastTetrahedralCorotationalForceField.cpp | 2 +- .../solidmechanics/fem/elastic/HexahedralFEMForceField.cpp | 2 +- .../fem/elastic/HexahedralFEMForceFieldAndMass.cpp | 2 +- .../solidmechanics/fem/elastic/HexahedronFEMForceField.cpp | 2 +- .../fem/elastic/HexahedronFEMForceFieldAndMass.cpp | 2 +- .../fem/elastic/LinearSmallStrainFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp | 2 +- .../fem/elastic/TetrahedralCorotationalFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/TriangleFEMForceField.cpp | 2 +- .../fem/elastic/TriangularAnisotropicFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/TriangularFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp index 5d85f22fc0c..45fec0e55ac 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp @@ -37,7 +37,7 @@ void registerBeamFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Beam finite elements.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index dd96bf86c23..33209a86d93 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -40,7 +40,7 @@ void registerComponent(sofa::core::ObjectFactory* factory) .withDescription(std::string(description)) .template addTemplateAttribute("dofType") .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp index 481dad11790..d7ba1537478 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp @@ -38,7 +38,7 @@ void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* fa .withModule(MODULE_NAME) .withDescription("Fast Corotational Tetrahedral Mesh.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp index 78d1df90907..6fb78a077c9 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp @@ -37,7 +37,7 @@ void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp index 6b7e25332ab..ab26774471c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp @@ -37,7 +37,7 @@ void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements with mass") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp index b6e0b8836dc..e1620a95768 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp @@ -37,7 +37,7 @@ void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp index cfdbca95630..67fd2186578 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp @@ -36,7 +36,7 @@ void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements with mass.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp index 7278cdbd6d9..4daa1c5d2eb 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp @@ -40,7 +40,7 @@ void registerComponent(sofa::core::ObjectFactory* factory) .withDescription(std::string(description)) .template addTemplateAttribute("dofType") .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp index 957df76be8a..b19e4dffe1f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp @@ -41,7 +41,7 @@ void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Bending Quad finite elements") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp index 8622a83bc0c..3fb365f0a7a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp @@ -38,7 +38,7 @@ void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* fac .withModule(MODULE_NAME) .withDescription("Corotational FEM Tetrahedral finite elements") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp index 62da36d5e96..f21c5947edf 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp @@ -38,7 +38,7 @@ void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Tetrahedral finite elements.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp index 4fb1d85606d..128437d296e 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp @@ -38,7 +38,7 @@ void registerTriangleFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Triangular finite elements for static topology.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp index e6507feccc1..1efc6196f8a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp @@ -36,7 +36,7 @@ void registerTriangularAnisotropicFEMForceField(sofa::core::ObjectFactory* facto .withModule(MODULE_NAME) .withDescription("Triangular finite element model using anisotropic material.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp index 125f7d795dd..20da0684241 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp @@ -39,7 +39,7 @@ void registerTriangularFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Corotational Triangular finite elements for dynamic topology.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp index 15badde543c..5c9a1daa438 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp @@ -37,7 +37,7 @@ void registerTriangularFEMForceFieldOptim(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Corotational Triangular finite elements.") .template addTemplateAttribute("dofType") - .withDeductionRule>() + .template withDeductionRule>() ); } From f698c08e996f37fbc575799fd36e768a429b0485 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 16:59:38 +0200 Subject: [PATCH 58/71] register BarycentricMapping using the legacy canCreate deduction rule --- .../component/mapping/linear/BarycentricMapping.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp b/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp index 8617f33bc88..33d21af2975 100644 --- a/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp +++ b/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp @@ -32,8 +32,14 @@ using namespace sofa::defaulttype; void registerBarycentricMapping(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Mapping using barycentric coordinates of the child with respect to cells of its parent.") - .add< BarycentricMapping< Vec3Types, Vec3Types > >(true)); + factory->registerComponent( + core::CreateComponent>("BarycentricMapping") + .withModule(MODULE_NAME) + .withDescription("Mapping using barycentric coordinates of the child with respect to cells of its parent.") + .template addTemplateAttribute("inDofType") + .template addTemplateAttribute("outDofType") + .template withDeductionRule>>() + ); } template class SOFA_COMPONENT_MAPPING_LINEAR_API BarycentricMapping< Vec3Types, Vec3Types >; From 1c711f8d9d38247f9ae5fb36f8dbfc335f9534bb Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 17:59:01 +0200 Subject: [PATCH 59/71] move to cpp --- .../Core/src/sofa/core/ComponentFactory.cpp | 18 ++++++++++++++++++ .../Core/src/sofa/core/ComponentFactory.h | 18 ++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 3faaab60f11..026ded73dbf 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -645,6 +645,24 @@ bool ComponentFactory::HasCreator(const std::string& classname) return MainComponentFactory::HasCreator(classname); } +ComponentFactory* MainComponentFactory::getInstance() +{ + static ComponentFactory instance; + return &instance; +} + +objectmodel::BaseComponent::SPtr MainComponentFactory::CreateComponent( + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + return getInstance()->createComponent(context, arg); +} + +objectmodel::BaseComponent::SPtr MainComponentFactory::CreateObject( + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + return CreateComponent(context, arg); +} + bool MainComponentFactory::HasCreator(const std::string& classname) { return getInstance()->hasCreator(classname); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index ff5fcac1aa5..4602d53c25d 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -138,22 +138,12 @@ std::string ComponentFactory::listClassesDerivedFrom(const std::string& separato struct SOFA_CORE_API MainComponentFactory { - static ComponentFactory* getInstance() - { - static ComponentFactory instance; - return &instance; - } + static ComponentFactory* getInstance(); - static objectmodel::BaseComponent::SPtr CreateComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) - { - return getInstance()->createComponent(context, arg); - } + static objectmodel::BaseComponent::SPtr CreateComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - //to deprecate - static objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) - { - return CreateComponent(context, arg); - } + // to deprecate + static objectmodel::BaseComponent::SPtr CreateObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); /// Test if a creator exists for a given classname static bool HasCreator(const std::string& classname); From a7ecdbe23783fee17c4db43c7bc1458023f4fb2e Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 18:10:08 +0200 Subject: [PATCH 60/71] add findComponent --- .../Core/src/sofa/core/ComponentFactory.cpp | 25 +++++++++++++------ .../Core/src/sofa/core/ComponentFactory.h | 2 ++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 026ded73dbf..9d1a8eb2762 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -454,7 +454,7 @@ using Filter = std::vector (*)(const std::vecto * component will be created based on this element. Otherwise, the filtered list is further filtered * based on deduction rules. */ -objectmodel::BaseComponent::SPtr applyFilter( +ComponentRegistrationData::SPtr applyFilter( const ComponentFactory& self, const std::string& componentName, const std::vector& candidates, @@ -469,7 +469,7 @@ objectmodel::BaseComponent::SPtr applyFilter( if (filteredCandidates.size() == 1) { // No ambiguity: The unique candidate is returned - return createComponentFrom(filteredCandidates.front(), context, arg); + return filteredCandidates.front(); } else { @@ -483,7 +483,7 @@ objectmodel::BaseComponent::SPtr applyFilter( << sofa::helper::join( deducedCandidates.begin(), deducedCandidates.end(), ", ") << "). The first one is selected."; - return createComponentFrom(deducedCandidates.front(), context, arg); + return deducedCandidates.front(); } else { @@ -495,7 +495,7 @@ objectmodel::BaseComponent::SPtr applyFilter( [](const ComponentRegistrationData::SPtr& desc){ std::stringstream ss; ss << *desc; return ss.str(); }, ", ") << "). The first one is selected."; // None of the deduction rules matches: returning the first filtered candidate - return createComponentFrom(filteredCandidates.front(), context, arg); + return filteredCandidates.front(); } } } @@ -504,8 +504,8 @@ objectmodel::BaseComponent::SPtr applyFilter( } -objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( - objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) + +ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::BaseObjectDescription* arg) { if (!arg) return nullptr; @@ -593,7 +593,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( // Final fallback if (!candidates.empty()) { - return createComponentFrom(candidates.front(), context, arg); + return candidates.front(); } // Final failure @@ -601,6 +601,17 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( return nullptr; } + +objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + if (auto component = findComponent(arg)) + { + return createComponentFrom(component, context, arg); + } + return nullptr; +} + objectmodel::BaseComponent::SPtr ComponentFactory::createObject( objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 4602d53c25d..9a90a80c216 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -42,6 +42,8 @@ class SOFA_CORE_API ComponentFactory void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData); + ComponentRegistrationData::SPtr findComponent(objectmodel::BaseObjectDescription* arg); + /// Create a component given a context and a description. objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); From 15cef2cef33006193489a3239af0b9ffd333fd38 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 20:43:58 +0200 Subject: [PATCH 61/71] fix --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 4 ++-- Sofa/framework/Core/src/sofa/core/ComponentFactory.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 9d1a8eb2762..1ca2bb4a61d 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -505,7 +505,7 @@ ComponentRegistrationData::SPtr applyFilter( } -ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::BaseObjectDescription* arg) +ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { if (!arg) return nullptr; @@ -605,7 +605,7 @@ ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::Bas objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { - if (auto component = findComponent(arg)) + if (auto component = findComponent(context, arg)) { return createComponentFrom(component, context, arg); } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 9a90a80c216..ff92fcff48e 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -42,7 +42,7 @@ class SOFA_CORE_API ComponentFactory void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData); - ComponentRegistrationData::SPtr findComponent(objectmodel::BaseObjectDescription* arg); + ComponentRegistrationData::SPtr findComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); /// Create a component given a context and a description. objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); From 7e10bb3c07efb829d99942ead4c9866ca04cf7ae Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 20:49:01 +0200 Subject: [PATCH 62/71] documentation --- .../Core/src/sofa/core/ComponentFactory.h | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index ff92fcff48e..6a5c88e0338 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -29,6 +29,13 @@ namespace sofa::core { +/** + * @brief Factory class for SOFA components. + * + * This class manages the registration and instantiation of SOFA components. + * It maintains a registry of ComponentRegistrationData which contains the creators + * and metadata for each component. + */ class SOFA_CORE_API ComponentFactory { public: @@ -36,37 +43,69 @@ class SOFA_CORE_API ComponentFactory using SOFA_CORE_DEPRECATED_OBJECTFACTORY_CLASSENTRY() ClassEntry = ComponentRegistrationData; using Registry = std::vector; + /** @brief Get the list of all registered components. */ const Registry& getRegistry() const { return m_registry; } + /** + * @brief Register components defined in a plugin. + * @param pluginName The name of the plugin to register. + * @return true if the registration was successful or already done, false if the plugin is not loaded. + */ bool registerObjectsFromPlugin(const std::string& pluginName); + /** + * @brief Register a component in the factory. + * @param componentRegistrationData The data containing component info and its creator. + */ void registerComponent(const ComponentRegistrationData::SPtr& componentRegistrationData); + /** + * @brief Find the most suitable component registration data for a given description. + * + * This method handles plugin auto-loading, alias resolution, and candidate selection + * based on template attributes and deduction rules. + * + * @param context The context in which the search is performed. + * @param arg The description of the object to find (must contain a "type" attribute). + * @return The matching ComponentRegistrationData, or nullptr if none found. + */ ComponentRegistrationData::SPtr findComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - /// Create a component given a context and a description. + /** + * @brief Create a component given a context and a description. + * + * This method first finds the appropriate component using @ref findComponent, + * then instantiates it, adds it to the context, and parses its attributes. + * + * @param context The context where the component will be added. + * @param arg The description of the component to create. + * @return A smart pointer to the created component, or nullptr on failure. + */ objectmodel::BaseComponent::SPtr createComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); - /// Test if a creator exists for a given classname + /** @brief Test if a creator exists for a given classname. */ bool hasCreator(const std::string& classname) const; - /// Fill the given vector with the registered classes from a given target + /** @brief Fill the given vector with the registered classes from a given target module/plugin. */ void getEntriesFromTarget(std::vector& result, const std::string& target) const; - /// Return the list of classes from a given target + /** @brief Return a string list of classes from a given target module/plugin. */ std::string listClassesFromTarget(std::string target, std::string separator = ", "); - /// Fill the given vector with all the registered classes derived from BaseClass + /** @brief Fill the given vector with all the registered classes derived from BaseClass. */ template std::vector getEntriesDerivedFrom() const; - /// Return the list of classes derived from BaseClass as a string + /** @brief Return the list of classes derived from BaseClass as a string. */ template std::string listClassesDerivedFrom(const std::string& separator = ", ") const; - objectmodel::BaseComponent::SPtr SOFA_CORE_DEPRECATED_OBJECTFACTORY_CREATEOBJECT() createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg); + /** + * @brief Legacy method to register multiple objects. + * @deprecated Use registerComponent instead. + */ SOFA_CORE_DEPRECATED_OBJECTFACTORY_REGISTEROBJECTS() bool registerObjects(LegacyComponentRegistrationData& ro); @@ -79,7 +118,6 @@ class SOFA_CORE_API ComponentFactory SOFA_ATTRIBUTE_DEPRECATED("v26.12", "v27.12", "Use MainComponentFactory::HasCreator instead") static bool HasCreator(const std::string& classname); - void getEntry(std::string) = delete; void getAllEntries(std::vector& result, bool filterUnloadedPlugins = true) = delete; @@ -136,8 +174,6 @@ std::string ComponentFactory::listClassesDerivedFrom(const std::string& separato } - - struct SOFA_CORE_API MainComponentFactory { static ComponentFactory* getInstance(); From c40888f3cfa3b8d760acca61de2ba8df0ad76da6 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 21:25:50 +0200 Subject: [PATCH 63/71] move into classes --- .../Core/src/sofa/core/ComponentFactory.cpp | 305 ++++++++++-------- 1 file changed, 165 insertions(+), 140 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 1ca2bb4a61d..41da0c51f93 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -262,136 +262,6 @@ void autoLoadPlugin(ComponentFactory& self, const std::string& pluginName) } } -std::vector selectCandidatesFromTemplateAttributes( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) -{ - SOFA_UNUSED(context); - - std::vector exactlyMatchingCandidates; - - for (const auto& candidate : candidates) - { - bool matchAllTemplateParameters = true; - for (const auto& [attribute, value] : candidate->templateAttributes) - { - const auto resolvedValue = defaulttype::TemplateAliases::resolveAlias(value); - const char* attr = arg->getAttribute(attribute, nullptr); - if (attr == nullptr) - { - matchAllTemplateParameters = false; - } - else - { - const std::string attrStr { attr }; - const auto resolvedAlias = defaulttype::TemplateAliases::resolveAlias(attrStr); - if (resolvedAlias != resolvedValue) - { - matchAllTemplateParameters = false; - } - } - } - - if (matchAllTemplateParameters) - { - exactlyMatchingCandidates.push_back(candidate); - } - - } - - return exactlyMatchingCandidates; -} - -std::vector selectCandidatesFromPartialTemplateAttributes( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) -{ - SOFA_UNUSED(context); - - std::vector partiallyMatchingCandidates; - - for (const auto& candidate : candidates) - { - for (const auto& [attribute, value] : candidate->templateAttributes) - { - const char* attr = arg->getAttribute(attribute, nullptr); - if (attr != nullptr) - { - const std::string attrStr { attr }; - if (defaulttype::TemplateAliases::resolveAlias(attrStr) == value) - { - partiallyMatchingCandidates.push_back(candidate); - break; - } - } - } - } - - return partiallyMatchingCandidates; -} - -std::vector selectCandidatesTemplateKeyword( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) -{ - SOFA_UNUSED(context); - - const char* templateAttr = arg->getAttribute("template", nullptr); - if (!templateAttr) - return {}; - - std::string templateAttrStr { templateAttr }; - templateAttrStr = defaulttype::TemplateAliases::resolveAlias(templateAttrStr); - - std::vector matchingCandidates; - - for (const auto& candidate : candidates) - { - const auto templateList = sofa::helper::join( - candidate->templateAttributes.begin(), candidate->templateAttributes.end(), - [](const auto& attr){ return defaulttype::TemplateAliases::resolveAlias(attr.second); }, ','); - if (templateAttrStr == templateList) - { - matchingCandidates.push_back(candidate); - } - } - - return matchingCandidates; -} - -std::vector noFilter( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) -{ - SOFA_UNUSED(context); - SOFA_UNUSED(arg); - return candidates; -} - -std::vector selectCandidatesDeductionRules( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) -{ - std::vector matchingCandidates; - - for (const auto& candidate : candidates) - { - // Check if the component has a rule AND if that rule applies. - if (const auto rule = candidate->templateDeductionRule; - rule && rule->doesComponentComplyWith(context, arg)) - { - matchingCandidates.push_back(candidate); - } - } - - return matchingCandidates; -} - objectmodel::BaseComponent::SPtr createComponentFrom(const ComponentRegistrationData::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { auto component = desc->creator->create(); @@ -447,7 +317,160 @@ bool knownIssues(const ComponentFactory& self, const std::string& clasName) return false; } -using Filter = std::vector (*)(const std::vector&, objectmodel::BaseContext*, objectmodel::BaseObjectDescription*); +class ComponentFilter +{ +public: + virtual ~ComponentFilter() = default; + virtual std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const = 0; +}; + +class ExactTemplateMatchFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override + { + SOFA_UNUSED(context); + + std::vector exactlyMatchingCandidates; + + for (const auto& candidate : candidates) + { + bool matchAllTemplateParameters = true; + for (const auto& [attribute, value] : candidate->templateAttributes) + { + const auto resolvedValue = defaulttype::TemplateAliases::resolveAlias(value); + const char* attr = arg->getAttribute(attribute, nullptr); + if (attr == nullptr) + { + matchAllTemplateParameters = false; + } + else + { + const std::string attrStr { attr }; + const auto resolvedAlias = defaulttype::TemplateAliases::resolveAlias(attrStr); + if (resolvedAlias != resolvedValue) + { + matchAllTemplateParameters = false; + } + } + } + + if (matchAllTemplateParameters) + { + exactlyMatchingCandidates.push_back(candidate); + } + } + + return exactlyMatchingCandidates; + } +}; + +class LegacyTemplateKeywordFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override + { + SOFA_UNUSED(context); + + const char* templateAttr = arg->getAttribute("template", nullptr); + if (!templateAttr) + return {}; + + std::string templateAttrStr { templateAttr }; + templateAttrStr = defaulttype::TemplateAliases::resolveAlias(templateAttrStr); + + std::vector matchingCandidates; + + for (const auto& candidate : candidates) + { + const auto templateList = sofa::helper::join( + candidate->templateAttributes.begin(), candidate->templateAttributes.end(), + [](const auto& attr){ return defaulttype::TemplateAliases::resolveAlias(attr.second); }, ','); + if (templateAttrStr == templateList) + { + matchingCandidates.push_back(candidate); + } + } + + return matchingCandidates; + } +}; + +class PartialTemplateMatchFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override + { + SOFA_UNUSED(context); + + std::vector partiallyMatchingCandidates; + + for (const auto& candidate : candidates) + { + for (const auto& [attribute, value] : candidate->templateAttributes) + { + const char* attr = arg->getAttribute(attribute, nullptr); + if (attr != nullptr) + { + const std::string attrStr { attr }; + if (defaulttype::TemplateAliases::resolveAlias(attrStr) == value) + { + partiallyMatchingCandidates.push_back(candidate); + break; + } + } + } + } + + return partiallyMatchingCandidates; + } +}; + +class NoFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override + { + SOFA_UNUSED(context); + SOFA_UNUSED(arg); + return candidates; + } +}; + +std::vector selectCandidatesDeductionRules( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) +{ + std::vector matchingCandidates; + + for (const auto& candidate : candidates) + { + // Check if the component has a rule AND if that rule applies. + if (const auto rule = candidate->templateDeductionRule; + rule && rule->doesComponentComplyWith(context, arg)) + { + matchingCandidates.push_back(candidate); + } + } + + return matchingCandidates; +} /** * Apply a filter on a list of potential candidates. If the filtered list has a unique element, a @@ -460,9 +483,9 @@ ComponentRegistrationData::SPtr applyFilter( const std::vector& candidates, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg, - Filter filter) + const ComponentFilter& filter) { - const auto filteredCandidates = filter(candidates, context, arg); + const auto filteredCandidates = filter.filter(candidates, context, arg); if (!filteredCandidates.empty()) { @@ -577,14 +600,16 @@ ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::Bas std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); - for (const Filter& filter : { - selectCandidatesFromTemplateAttributes, //Exact Template Match (Highest Priority). - selectCandidatesTemplateKeyword, //Selection by Legacy 'template' Keyword (Medium-High Priority) - selectCandidatesFromPartialTemplateAttributes, //Partial Template Matching (Medium Priority) - noFilter //General Template Deduction (Lowest Priority) - }) + static std::array, 4> filters { + std::make_unique(), //Exact Template Match (Highest Priority). + std::make_unique(), //Selection by Legacy 'template' Keyword (Medium-High Priority) + std::make_unique(), //Partial Template Matching (Medium Priority) + std::make_unique() //General Template Deduction (Lowest Priority) + }; + + for (const auto& filter : filters) { - if (auto component = applyFilter(*this, componentName, candidates, context, arg, filter)) + if (auto component = applyFilter(*this, componentName, candidates, context, arg, *filter)) { return component; } From f45d1b60a79d57db277d59044779333a6d1057a6 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 21:29:50 +0200 Subject: [PATCH 64/71] move to files --- Sofa/framework/Core/CMakeLists.txt | 2 + .../Core/src/sofa/core/ComponentFactory.cpp | 136 +----------------- .../src/sofa/core/ComponentFactoryFilters.cpp | 132 +++++++++++++++++ .../src/sofa/core/ComponentFactoryFilters.h | 76 ++++++++++ 4 files changed, 211 insertions(+), 135 deletions(-) create mode 100644 Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.cpp create mode 100644 Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h diff --git a/Sofa/framework/Core/CMakeLists.txt b/Sofa/framework/Core/CMakeLists.txt index 242116f48e5..30036392f69 100644 --- a/Sofa/framework/Core/CMakeLists.txt +++ b/Sofa/framework/Core/CMakeLists.txt @@ -33,6 +33,7 @@ set(HEADER_FILES ${SRC_ROOT}/CollisionModel.h ${SRC_ROOT}/ComponentCreator.h ${SRC_ROOT}/ComponentFactory.h + ${SRC_ROOT}/ComponentFactoryFilters.h ${SRC_ROOT}/ComponentLibrary.h ${SRC_ROOT}/ComponentNameHelper.h ${SRC_ROOT}/ComponentRegistrationData.h @@ -237,6 +238,7 @@ set(SOURCE_FILES ${SRC_ROOT}/CategoryLibrary.cpp ${SRC_ROOT}/CollisionModel.cpp ${SRC_ROOT}/ComponentFactory.cpp + ${SRC_ROOT}/ComponentFactoryFilters.cpp ${SRC_ROOT}/ComponentLibrary.cpp ${SRC_ROOT}/ComponentNameHelper.cpp ${SRC_ROOT}/ConstraintParams.cpp diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 41da0c51f93..cc962abcb41 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -20,6 +20,7 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #include +#include #include #include #include @@ -317,141 +318,6 @@ bool knownIssues(const ComponentFactory& self, const std::string& clasName) return false; } -class ComponentFilter -{ -public: - virtual ~ComponentFilter() = default; - virtual std::vector filter( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) const = 0; -}; - -class ExactTemplateMatchFilter final : public ComponentFilter -{ -public: - std::vector filter( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) const override - { - SOFA_UNUSED(context); - - std::vector exactlyMatchingCandidates; - - for (const auto& candidate : candidates) - { - bool matchAllTemplateParameters = true; - for (const auto& [attribute, value] : candidate->templateAttributes) - { - const auto resolvedValue = defaulttype::TemplateAliases::resolveAlias(value); - const char* attr = arg->getAttribute(attribute, nullptr); - if (attr == nullptr) - { - matchAllTemplateParameters = false; - } - else - { - const std::string attrStr { attr }; - const auto resolvedAlias = defaulttype::TemplateAliases::resolveAlias(attrStr); - if (resolvedAlias != resolvedValue) - { - matchAllTemplateParameters = false; - } - } - } - - if (matchAllTemplateParameters) - { - exactlyMatchingCandidates.push_back(candidate); - } - } - - return exactlyMatchingCandidates; - } -}; - -class LegacyTemplateKeywordFilter final : public ComponentFilter -{ -public: - std::vector filter( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) const override - { - SOFA_UNUSED(context); - - const char* templateAttr = arg->getAttribute("template", nullptr); - if (!templateAttr) - return {}; - - std::string templateAttrStr { templateAttr }; - templateAttrStr = defaulttype::TemplateAliases::resolveAlias(templateAttrStr); - - std::vector matchingCandidates; - - for (const auto& candidate : candidates) - { - const auto templateList = sofa::helper::join( - candidate->templateAttributes.begin(), candidate->templateAttributes.end(), - [](const auto& attr){ return defaulttype::TemplateAliases::resolveAlias(attr.second); }, ','); - if (templateAttrStr == templateList) - { - matchingCandidates.push_back(candidate); - } - } - - return matchingCandidates; - } -}; - -class PartialTemplateMatchFilter final : public ComponentFilter -{ -public: - std::vector filter( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) const override - { - SOFA_UNUSED(context); - - std::vector partiallyMatchingCandidates; - - for (const auto& candidate : candidates) - { - for (const auto& [attribute, value] : candidate->templateAttributes) - { - const char* attr = arg->getAttribute(attribute, nullptr); - if (attr != nullptr) - { - const std::string attrStr { attr }; - if (defaulttype::TemplateAliases::resolveAlias(attrStr) == value) - { - partiallyMatchingCandidates.push_back(candidate); - break; - } - } - } - } - - return partiallyMatchingCandidates; - } -}; - -class NoFilter final : public ComponentFilter -{ -public: - std::vector filter( - const std::vector& candidates, - objectmodel::BaseContext* context, - objectmodel::BaseObjectDescription* arg) const override - { - SOFA_UNUSED(context); - SOFA_UNUSED(arg); - return candidates; - } -}; - std::vector selectCandidatesDeductionRules( const std::vector& candidates, objectmodel::BaseContext* context, diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.cpp new file mode 100644 index 00000000000..b8140f865d8 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.cpp @@ -0,0 +1,132 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include + +namespace sofa::core +{ + +std::vector ExactTemplateMatchFilter::filter( + const std::vector& candidates, + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) const +{ + SOFA_UNUSED(context); + + std::vector exactlyMatchingCandidates; + + for (const auto& candidate : candidates) + { + bool matchAllTemplateParameters = true; + for (const auto& [attribute, value] : candidate->templateAttributes) + { + const auto resolvedValue = defaulttype::TemplateAliases::resolveAlias(value); + const char* attr = arg->getAttribute(attribute, nullptr); + if (attr == nullptr) + { + matchAllTemplateParameters = false; + } + else + { + const std::string attrStr{attr}; + const auto resolvedAlias = defaulttype::TemplateAliases::resolveAlias(attrStr); + if (resolvedAlias != resolvedValue) + { + matchAllTemplateParameters = false; + } + } + } + + if (matchAllTemplateParameters) + { + exactlyMatchingCandidates.push_back(candidate); + } + } + + return exactlyMatchingCandidates; +} + +std::vector LegacyTemplateKeywordFilter::filter( + const std::vector& candidates, + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) const +{ + SOFA_UNUSED(context); + + const char* templateAttr = arg->getAttribute("template", nullptr); + if (!templateAttr) return {}; + + std::string templateAttrStr{templateAttr}; + templateAttrStr = defaulttype::TemplateAliases::resolveAlias(templateAttrStr); + + std::vector matchingCandidates; + + for (const auto& candidate : candidates) + { + const auto templateList = sofa::helper::join( + candidate->templateAttributes.begin(), candidate->templateAttributes.end(), + [](const auto& attr) + { return defaulttype::TemplateAliases::resolveAlias(attr.second); }, ','); + if (templateAttrStr == templateList) + { + matchingCandidates.push_back(candidate); + } + } + + return matchingCandidates; +} + +std::vector PartialTemplateMatchFilter::filter( + const std::vector& candidates, + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) const +{ + SOFA_UNUSED(context); + + std::vector partiallyMatchingCandidates; + + for (const auto& candidate : candidates) + { + for (const auto& [attribute, value] : candidate->templateAttributes) + { + const char* attr = arg->getAttribute(attribute, nullptr); + if (attr != nullptr) + { + const std::string attrStr{attr}; + if (defaulttype::TemplateAliases::resolveAlias(attrStr) == value) + { + partiallyMatchingCandidates.push_back(candidate); + break; + } + } + } + } + + return partiallyMatchingCandidates; +} + +std::vector NoFilter::filter( + const std::vector& candidates, + objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) const +{ + SOFA_UNUSED(context); + SOFA_UNUSED(arg); + return candidates; +} +} // namespace sofa::core diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h b/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h new file mode 100644 index 00000000000..46a3f4e5b86 --- /dev/null +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h @@ -0,0 +1,76 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include + +namespace sofa::core +{ + + +class ComponentFilter +{ +public: + virtual ~ComponentFilter() = default; + virtual std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const = 0; +}; + +class ExactTemplateMatchFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override; +}; + +class LegacyTemplateKeywordFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override; +}; + +class PartialTemplateMatchFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override; +}; + +class NoFilter final : public ComponentFilter +{ +public: + std::vector filter( + const std::vector& candidates, + objectmodel::BaseContext* context, + objectmodel::BaseObjectDescription* arg) const override; +}; + + +} From 26e006a962d4ffefaa3e27359a20f13e0554c1db Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Wed, 1 Jul 2026 21:44:12 +0200 Subject: [PATCH 65/71] decomposition --- .../Core/src/sofa/core/ComponentFactory.cpp | 131 +++++++++--------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index cc962abcb41..4d081866538 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -263,27 +263,6 @@ void autoLoadPlugin(ComponentFactory& self, const std::string& pluginName) } } -objectmodel::BaseComponent::SPtr createComponentFrom(const ComponentRegistrationData::SPtr& desc, objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) -{ - auto component = desc->creator->create(); - - if (component) - { - if (context) - { - context->addObject(component); - } - - msg_warning_when(desc->componentModule.empty(), component.get()) << "Module name is empty"; - - component->parse(arg); - - component->d_factoryName.setValue(desc->componentName); - } - - return component; -} - std::vector similarComponentNames(const ComponentFactory& self, const std::string& className) { std::set allClassNames; @@ -391,27 +370,60 @@ ComponentRegistrationData::SPtr applyFilter( return nullptr; } -} +void reportMissingComponents(ComponentFactory& self, const std::string& componentName) +{ + std::stringstream ss; + ss << "Cannot create component '" << componentName << "': '" << componentName << "' not found in the factory registry."; + const auto similarNames = similarComponentNames(self, componentName); + if (!similarNames.empty()) + { + ss << " Suggestion: Components were found with similar names: " << sofa::helper::join(similarNames, ", "); + } -ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) + msg_error(&self) << ss.str(); +} + +bool filterUnloadedPluginsCandidates(const ComponentFactory& self, + const std::string& componentName, + const std::vector& candidates) { - if (!arg) - return nullptr; + auto candidatesWithoutUnloadedPlugins = candidates; + std::erase_if(candidatesWithoutUnloadedPlugins, [](const ComponentRegistrationData::SPtr& candidate) + { + return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); + }); + + if (candidatesWithoutUnloadedPlugins.empty()) + { + std::set unloadedPlugins; + std::transform(candidates.begin(), candidates.end(), std::inserter(unloadedPlugins, unloadedPlugins.begin()), + [](const ComponentRegistrationData::SPtr& component) { return component->componentModule; }); + const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end(), ", "); + msg_error(&self) << "Attempted to create component '" << componentName + << "' but all potential candidates rely on component from currently unloaded plugins:" << unloadedPluginsString << "]"; + return false; + } + return true; +} - const char* typeAttribute = arg->getAttribute( "type", nullptr); +} - if (typeAttribute == nullptr) - return nullptr; - std::string componentName, moduleName; - extractModuleName(std::string{typeAttribute}, componentName, moduleName); +ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) +{ + if (!arg) return nullptr; - if (knownIssues(*this, componentName)) + std::string componentName, moduleName; { - return nullptr; + const char* typeAttribute = arg->getAttribute( "type", nullptr); + if (typeAttribute == nullptr) return nullptr; + extractModuleName(std::string{typeAttribute}, componentName, moduleName); } + if (knownIssues(*this, componentName)) return nullptr; + + // 1. Ensure plugins are loaded if (auto it = helper::lifecycle::movedComponents.find(componentName); it != helper::lifecycle::movedComponents.end()) { @@ -423,49 +435,27 @@ ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::Bas autoLoadPlugin(*this, moduleName); } + // 2. Get initial candidates std::vector candidates = getComponentsFromName(*this, componentName, moduleName); - - // Early failure because there are no compatible candidates if (candidates.empty()) { - std::stringstream ss; - ss << "Cannot create component '" << componentName << "': '" << componentName << "' not found in the factory registry."; - - const auto similarNames = similarComponentNames(*this, componentName); - if (!similarNames.empty()) - { - ss << " Suggestion: Components were found with similar names: " << sofa::helper::join(similarNames, ", "); - } - - msg_error() << ss.str(); + reportMissingComponents(*this, componentName); return nullptr; } - // Check that the candidates don't rely on unloaded modules + // 3. Filter out candidates from unloaded plugins + if (!filterUnloadedPluginsCandidates(*this, componentName, candidates)) { - auto candidatesWithoutUnloadedPlugins = candidates; - std::erase_if(candidatesWithoutUnloadedPlugins, [](const ComponentRegistrationData::SPtr& candidate) - { - return helper::system::PluginManager::getInstance().isPluginUnloaded(candidate->componentModule); - }); - - if (candidatesWithoutUnloadedPlugins.empty()) - { - std::set unloadedPlugins; - std::transform(candidates.begin(), candidates.end(), std::inserter(unloadedPlugins, unloadedPlugins.begin()), - [](const ComponentRegistrationData::SPtr& component) { return component->componentModule; }); - const auto unloadedPluginsString = sofa::helper::join(unloadedPlugins.begin(), unloadedPlugins.end(), ", "); - msg_error() << "Attempted to create component '" << componentName - << "' but all potential candidates rely on component from currently unloaded plugins:" << unloadedPluginsString << "]"; - return nullptr; - } + return nullptr; } + // 4. Sort by priority // In case of ambiguity (multiple candidates), sorting candidates will allow returning the // component with the highest priority. std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) { return a->instantiationPriority > b->instantiationPriority; }); + // 5. Apply template matching filters static std::array, 4> filters { std::make_unique(), //Exact Template Match (Highest Priority). std::make_unique(), //Selection by Legacy 'template' Keyword (Medium-High Priority) @@ -496,9 +486,24 @@ ComponentRegistrationData::SPtr ComponentFactory::findComponent(objectmodel::Bas objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { - if (auto component = findComponent(context, arg)) + if (auto componentData = findComponent(context, arg)) { - return createComponentFrom(component, context, arg); + auto component = componentData->creator->create(); + + if (component) + { + if (context) + { + context->addObject(component); + } + + msg_warning_when(componentData->componentModule.empty(), component.get()) << "Module name is empty"; + + component->parse(arg); + component->d_factoryName.setValue(componentData->componentName); + } + + return component; } return nullptr; } From 2d0bc759b740377076b4cdfe3da9bc2f49e691a0 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 2 Jul 2026 09:16:41 +0200 Subject: [PATCH 66/71] add deleted method --- Sofa/framework/Core/src/sofa/core/ComponentFactory.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 6a5c88e0338..3a44d2311fa 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -121,6 +121,8 @@ class SOFA_CORE_API ComponentFactory void getEntry(std::string) = delete; void getAllEntries(std::vector& result, bool filterUnloadedPlugins = true) = delete; + std::string shortName(std::string classname) = delete; + void dumpXML(std::ostream& out = std::cout) = delete; void dumpHTML(std::ostream& out = std::cout) = delete; From deecea4f71351c4a556c6ebe1bd1362c6517188c Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 2 Jul 2026 09:16:55 +0200 Subject: [PATCH 67/71] make it const --- Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp | 2 +- Sofa/framework/Core/src/sofa/core/ComponentFactory.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index 4d081866538..eb295a5ee67 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -532,7 +532,7 @@ void ComponentFactory::getEntriesFromTarget(std::vector entries; this->getEntriesFromTarget(entries, target); diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index 3a44d2311fa..f21b5c5e386 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -90,7 +90,7 @@ class SOFA_CORE_API ComponentFactory void getEntriesFromTarget(std::vector& result, const std::string& target) const; /** @brief Return a string list of classes from a given target module/plugin. */ - std::string listClassesFromTarget(std::string target, std::string separator = ", "); + std::string listClassesFromTarget(std::string target, std::string separator = ", ") const; /** @brief Fill the given vector with all the registered classes derived from BaseClass. */ template From 6b93015bd914f30e79692e2992423ed9fbaba4d9 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 2 Jul 2026 09:46:06 +0200 Subject: [PATCH 68/71] set all the registration data in BaseComponent --- Sofa/framework/Core/src/sofa/core/ComponentCreator.h | 11 +++++++---- .../framework/Core/src/sofa/core/ComponentFactory.cpp | 2 +- Sofa/framework/Core/src/sofa/core/ComponentFactory.h | 1 + .../Core/src/sofa/core/ComponentFactoryFilters.h | 1 + .../Core/src/sofa/core/objectmodel/BaseComponent.cpp | 3 --- .../Core/src/sofa/core/objectmodel/BaseComponent.h | 11 ++++++++--- Sofa/framework/Core/test/ComponentFactory_test.cpp | 6 +++--- 7 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h index 9cca115d0bb..058d8d1144f 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentCreator.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentCreator.h @@ -22,8 +22,11 @@ #pragma once #include +#include +#include +#include -#include +namespace sofa::core::objectmodel { class BaseComponent; } namespace sofa::core { @@ -43,7 +46,7 @@ struct SOFA_CORE_API BaseComponentCreator * Used by ComponentFactory to generate object instances before attribute parsing. * @return A shared pointer to the newly created BaseComponent. */ - virtual objectmodel::BaseComponent::SPtr create() const = 0; + virtual sofa::core::sptr create() const = 0; /** * @brief Creates a copy of the creator. @@ -63,11 +66,11 @@ struct SOFA_CORE_API BaseComponentCreator template struct ComponentCreator : public BaseComponentCreator { - objectmodel::BaseComponent::SPtr create() const override + sofa::core::sptr create() const override { // WARNING: // It obliges the class to have a default constructor - return sofa::core::objectmodel::New(); + return objectmodel::New(); } std::unique_ptr clone() const override diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp index eb295a5ee67..fa47b8ab7d6 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.cpp @@ -500,7 +500,7 @@ objectmodel::BaseComponent::SPtr ComponentFactory::createComponent( msg_warning_when(componentData->componentModule.empty(), component.get()) << "Module name is empty"; component->parse(arg); - component->d_factoryName.setValue(componentData->componentName); + component->m_factoryRegistrationData = componentData; } return component; diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h index f21b5c5e386..4547efb6f97 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactory.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace sofa::core { diff --git a/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h b/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h index 46a3f4e5b86..7f2e6d8a936 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentFactoryFilters.h @@ -21,6 +21,7 @@ ******************************************************************************/ #pragma once #include +#include namespace sofa::core { diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp index 86906a1916b..656955cdd71 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.cpp @@ -38,7 +38,6 @@ BaseComponent::BaseComponent() , l_context(initLink("context","Graph Node containing this object (or BaseContext::getDefault() if no graph is used)")) , l_slaves(initLink("slaves","Sub-objects used internally by this object")) , l_master(initLink("master","nullptr for regular objects, or master object for which this object is one sub-objects")) - , d_factoryName(initData(&d_factoryName, std::string{}, "factoryName", "Component name registered into the factory")) { auto bindChangeContextLink = [this](auto&& before, auto&& after) { return this->changeContextLink(before, after); }; l_context.setValidator(bindChangeContextLink); @@ -47,8 +46,6 @@ BaseComponent::BaseComponent() auto bindChangeSlavesLink = [this](auto&& ptr, auto&& index, auto&& add) { return this->changeSlavesLink(ptr, index, add); }; l_slaves.setValidator(bindChangeSlavesLink); f_listening.setAutoLink(false); - - d_factoryName.setReadOnly(true); } BaseComponent::~BaseComponent() diff --git a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h index c6587786c8e..eaef54a53b1 100644 --- a/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h +++ b/Sofa/framework/Core/src/sofa/core/objectmodel/BaseComponent.h @@ -21,9 +21,10 @@ ******************************************************************************/ #pragma once -#include +#include #include #include +#include namespace sofa::core::objectmodel { @@ -43,6 +44,8 @@ class SOFA_CORE_API BaseComponent : public virtual Base SOFA_CLASS(BaseComponent, Base); SOFA_BASE_CAST_IMPLEMENTATION(BaseComponent) + friend class ComponentFactory; + protected: BaseComponent(); @@ -184,8 +187,6 @@ class SOFA_CORE_API BaseComponent : public virtual Base Base* findLinkDestClass(const BaseClass* destType, const std::string& path, const BaseLink* link) override; - Data d_factoryName; - /// Return the full path name of this object virtual std::string getPathName() const override; @@ -223,9 +224,13 @@ class SOFA_CORE_API BaseComponent : public virtual Base /// BaseNode can set the context of its own objects friend class BaseNode; +private: + ComponentRegistrationData::SPtr m_factoryRegistrationData; public: + ComponentRegistrationData::SPtr getFactoryRegistrationData() const { return m_factoryRegistrationData; } + /// the component can insert itself directly in the right sequence in the Node /// so the Node does not have to test its type against all known types /// \returns true if the component was inserted diff --git a/Sofa/framework/Core/test/ComponentFactory_test.cpp b/Sofa/framework/Core/test/ComponentFactory_test.cpp index 9c370651446..ca38aa6d7cd 100644 --- a/Sofa/framework/Core/test/ComponentFactory_test.cpp +++ b/Sofa/framework/Core/test/ComponentFactory_test.cpp @@ -86,7 +86,7 @@ TEST_F(ComponentFactory_test, CreateComponent) ASSERT_NE(createdComponent, nullptr); EXPECT_EQ(createdComponent->getName(), "nameInTheScene"); EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); - EXPECT_EQ(createdComponent->d_factoryName.getValue(), "DummyComponent"); + EXPECT_EQ(createdComponent->getFactoryRegistrationData()->componentName, "DummyComponent"); } TEST_F(ComponentFactory_test, CreateComponentDifferentName) @@ -101,7 +101,7 @@ TEST_F(ComponentFactory_test, CreateComponentDifferentName) ASSERT_NE(createdComponent, nullptr); EXPECT_EQ(createdComponent->getName(), "nameInTheScene"); EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); - EXPECT_EQ(createdComponent->d_factoryName.getValue(), "NotDummyComponent"); + EXPECT_EQ(createdComponent->getFactoryRegistrationData()->componentName, "NotDummyComponent"); } TEST_F(ComponentFactory_test, HasCreator) @@ -352,7 +352,7 @@ TEST_F(ComponentFactory_test, FullNameCreation) auto createdComponent = factory.createComponent(node.get(), &desc); ASSERT_NE(createdComponent, nullptr); EXPECT_EQ(createdComponent->getClassName(), "DummyComponent"); - EXPECT_EQ(createdComponent->d_factoryName.getValue(), "MyComponent"); + EXPECT_EQ(createdComponent->getFactoryRegistrationData()->componentName, "MyComponent"); } TEST_F(ComponentFactory_test, SuggestionOnMisspell) From 9aaeb8e29834bf220e15dd07418ec6eddef13f2e Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 2 Jul 2026 11:21:18 +0200 Subject: [PATCH 69/71] deduce class name --- .../component/linearsolver/direct/SparseLDLSolver.cpp | 2 +- .../sofa/component/mapping/linear/BarycentricMapping.cpp | 2 +- .../solidmechanics/fem/elastic/BeamFEMForceField.cpp | 2 +- .../fem/elastic/CorotationalFEMForceField.cpp | 2 +- .../fem/elastic/FastTetrahedralCorotationalForceField.cpp | 2 +- .../fem/elastic/HexahedralFEMForceField.cpp | 2 +- .../fem/elastic/HexahedralFEMForceFieldAndMass.cpp | 2 +- .../fem/elastic/HexahedronFEMForceField.cpp | 2 +- .../fem/elastic/HexahedronFEMForceFieldAndMass.cpp | 2 +- .../fem/elastic/LinearSmallStrainFEMForceField.cpp | 2 +- .../fem/elastic/QuadBendingFEMForceField.cpp | 2 +- .../fem/elastic/TetrahedralCorotationalFEMForceField.cpp | 2 +- .../fem/elastic/TetrahedronFEMForceField.cpp | 2 +- .../solidmechanics/fem/elastic/TriangleFEMForceField.cpp | 2 +- .../fem/elastic/TriangularAnisotropicFEMForceField.cpp | 2 +- .../fem/elastic/TriangularFEMForceField.cpp | 2 +- .../fem/elastic/TriangularFEMForceFieldOptim.cpp | 2 +- .../Core/src/sofa/core/ComponentRegistrationData.h | 8 ++++++++ 18 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp index f5c3c94b03f..c757d91a13c 100644 --- a/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp +++ b/Sofa/Component/LinearSolver/Direct/src/sofa/component/linearsolver/direct/SparseLDLSolver.cpp @@ -35,7 +35,7 @@ template void registerComponent(sofa::core::ComponentFactory* factory) { factory->registerComponent( - core::CreateComponent>("SparseLDLSolver") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription(std::string(description)) .template addTemplateAttribute("matrixType") diff --git a/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp b/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp index 33d21af2975..a3d429fb178 100644 --- a/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp +++ b/Sofa/Component/Mapping/Linear/src/sofa/component/mapping/linear/BarycentricMapping.cpp @@ -33,7 +33,7 @@ using namespace sofa::defaulttype; void registerBarycentricMapping(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("BarycentricMapping") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Mapping using barycentric coordinates of the child with respect to cells of its parent.") .template addTemplateAttribute("inDofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp index 45fec0e55ac..349efc5b2e6 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp @@ -33,7 +33,7 @@ using namespace sofa::defaulttype; void registerBeamFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("BeamFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Beam finite elements.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index 33209a86d93..9116df7a3dc 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -35,7 +35,7 @@ template void registerComponent(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("CorotationalFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription(std::string(description)) .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp index d7ba1537478..22baae50dd5 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp @@ -34,7 +34,7 @@ using namespace sofa::defaulttype; void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("FastTetrahedralCorotationalForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Fast Corotational Tetrahedral Mesh.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp index 6fb78a077c9..44fbf5eb764 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp @@ -33,7 +33,7 @@ using namespace sofa::defaulttype; void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("HexahedralFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp index ab26774471c..c676ea1000c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp @@ -33,7 +33,7 @@ using namespace sofa::defaulttype; void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("HexahedralFEMForceFieldAndMass") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements with mass") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp index e1620a95768..1bab0ed191f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp @@ -33,7 +33,7 @@ using namespace sofa::defaulttype; void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("HexahedronFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp index 67fd2186578..213f3dd51b4 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp @@ -32,7 +32,7 @@ using namespace sofa::defaulttype; void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("HexahedronFEMForceFieldAndMass") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements with mass.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp index 4daa1c5d2eb..570b282020a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp @@ -35,7 +35,7 @@ template void registerComponent(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("CorotationalFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription(std::string(description)) .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp index b19e4dffe1f..743bb07f3a5 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp @@ -37,7 +37,7 @@ using namespace sofa::defaulttype; void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("QuadBendingFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Bending Quad finite elements") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp index 3fb365f0a7a..04589fdf115 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp @@ -34,7 +34,7 @@ using namespace sofa::defaulttype; void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("TetrahedralCorotationalFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Corotational FEM Tetrahedral finite elements") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp index f21c5947edf..9599f309ffd 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp @@ -34,7 +34,7 @@ using namespace sofa::defaulttype; void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("TetrahedronFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Tetrahedral finite elements.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp index 128437d296e..fbabb2c0738 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp @@ -34,7 +34,7 @@ using namespace sofa::defaulttype; void registerTriangleFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("TriangleFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Triangular finite elements for static topology.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp index 1efc6196f8a..f750d73dce0 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp @@ -32,7 +32,7 @@ using namespace sofa::defaulttype; void registerTriangularAnisotropicFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("TriangularAnisotropicFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Triangular finite element model using anisotropic material.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp index 20da0684241..d726a7d4e9a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp @@ -35,7 +35,7 @@ using namespace sofa::defaulttype; void registerTriangularFEMForceField(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("TriangularFEMForceField") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Corotational Triangular finite elements for dynamic topology.") .template addTemplateAttribute("dofType") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp index 5c9a1daa438..444c1350799 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp @@ -33,7 +33,7 @@ using namespace sofa::defaulttype; void registerTriangularFEMForceFieldOptim(sofa::core::ObjectFactory* factory) { factory->registerComponent( - core::CreateComponent>("TriangularFEMForceFieldOptim") + core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Corotational Triangular finite elements.") .template addTemplateAttribute("dofType") diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 9997ce44e68..59f01245044 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -192,6 +192,14 @@ ComponentRegistrationDataName CreateComponent(const std::string& componentName) return ComponentRegistrationDataName(componentName, std::move(creator), classData); } +template +ComponentRegistrationDataName CreateComponent() +{ + std::unique_ptr creator = std::make_unique>(); + BaseClass* classData = Component::GetClass(); + return ComponentRegistrationDataName(classData->className, std::move(creator), classData); +} + /**************************************************************************************************/ From 886671329f6a509d0fb46e0399bfb0e482fc95d4 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 2 Jul 2026 12:24:05 +0200 Subject: [PATCH 70/71] template deduction rule can be deduced from base class --- .../fem/elastic/BeamFEMForceField.cpp | 1 - .../fem/elastic/CorotationalFEMForceField.cpp | 1 - .../FastTetrahedralCorotationalForceField.cpp | 1 - .../fem/elastic/HexahedralFEMForceField.cpp | 1 - .../HexahedralFEMForceFieldAndMass.cpp | 1 - .../fem/elastic/HexahedronFEMForceField.cpp | 1 - .../HexahedronFEMForceFieldAndMass.cpp | 1 - .../LinearSmallStrainFEMForceField.cpp | 1 - .../fem/elastic/QuadBendingFEMForceField.cpp | 1 - .../TetrahedralCorotationalFEMForceField.cpp | 1 - .../fem/elastic/TetrahedronFEMForceField.cpp | 1 - .../fem/elastic/TriangleFEMForceField.cpp | 1 - .../TriangularAnisotropicFEMForceField.cpp | 1 - .../fem/elastic/TriangularFEMForceField.cpp | 1 - .../elastic/TriangularFEMForceFieldOptim.cpp | 1 - .../src/sofa/core/ComponentRegistrationData.h | 99 ++++++++++--------- .../Core/src/sofa/core/behavior/ForceField.h | 3 + 17 files changed, 53 insertions(+), 64 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp index 349efc5b2e6..b7f21ba5b69 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp @@ -37,7 +37,6 @@ void registerBeamFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Beam finite elements.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp index 9116df7a3dc..1b3ba139284 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/CorotationalFEMForceField.cpp @@ -40,7 +40,6 @@ void registerComponent(sofa::core::ObjectFactory* factory) .withDescription(std::string(description)) .template addTemplateAttribute("dofType") .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp index 22baae50dd5..d5fb127ebc7 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FastTetrahedralCorotationalForceField.cpp @@ -38,7 +38,6 @@ void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* fa .withModule(MODULE_NAME) .withDescription("Fast Corotational Tetrahedral Mesh.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp index 44fbf5eb764..27f37b22f25 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceField.cpp @@ -37,7 +37,6 @@ void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp index c676ea1000c..0e9dad8b04f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedralFEMForceFieldAndMass.cpp @@ -37,7 +37,6 @@ void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements with mass") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp index 1bab0ed191f..25711fe4896 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceField.cpp @@ -37,7 +37,6 @@ void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp index 213f3dd51b4..8df4e713051 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/HexahedronFEMForceFieldAndMass.cpp @@ -36,7 +36,6 @@ void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Hexahedral finite elements with mass.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp index 570b282020a..5feccd9e48c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/LinearSmallStrainFEMForceField.cpp @@ -40,7 +40,6 @@ void registerComponent(sofa::core::ObjectFactory* factory) .withDescription(std::string(description)) .template addTemplateAttribute("dofType") .addTemplateAttribute("elementType", sofa::geometry::elementTypeToString(ElementType::Element_type)) - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp index 743bb07f3a5..1b43e6572ac 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadBendingFEMForceField.cpp @@ -41,7 +41,6 @@ void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Bending Quad finite elements") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp index 04589fdf115..4a014b40729 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedralCorotationalFEMForceField.cpp @@ -38,7 +38,6 @@ void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* fac .withModule(MODULE_NAME) .withDescription("Corotational FEM Tetrahedral finite elements") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp index 9599f309ffd..613be7440b3 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TetrahedronFEMForceField.cpp @@ -38,7 +38,6 @@ void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Tetrahedral finite elements.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp index fbabb2c0738..dcbfbc5103d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.cpp @@ -38,7 +38,6 @@ void registerTriangleFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Triangular finite elements for static topology.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp index f750d73dce0..29c2f854a56 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularAnisotropicFEMForceField.cpp @@ -36,7 +36,6 @@ void registerTriangularAnisotropicFEMForceField(sofa::core::ObjectFactory* facto .withModule(MODULE_NAME) .withDescription("Triangular finite element model using anisotropic material.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp index d726a7d4e9a..e5896fca806 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceField.cpp @@ -39,7 +39,6 @@ void registerTriangularFEMForceField(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Corotational Triangular finite elements for dynamic topology.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp index 444c1350799..28a5750ba1e 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangularFEMForceFieldOptim.cpp @@ -37,7 +37,6 @@ void registerTriangularFEMForceFieldOptim(sofa::core::ObjectFactory* factory) .withModule(MODULE_NAME) .withDescription("Corotational Triangular finite elements.") .template addTemplateAttribute("dofType") - .template withDeductionRule>() ); } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 59f01245044..6981bbcd2f8 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -65,19 +65,22 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder { ComponentRegistrationData::SPtr data; - ComponentRegistrationDataBuilder( - const std::string& componentName, - const std::string& moduleName, - const std::string& description, - std::unique_ptr creator, - const BaseClass* classData) - : data(std::make_shared()) + ComponentRegistrationDataBuilder& withName(const std::string& name) + { + data->componentName = name; + return *this; + } + + ComponentRegistrationDataBuilder& withModule(const std::string& componentModule) + { + data->componentModule = componentModule; + return *this; + } + + ComponentRegistrationDataBuilder& withDescription(const std::string& description) { - data->componentName = componentName; - data->componentModule = moduleName; data->description = description; - data->creator = std::move(creator); - data->classData = classData; + return *this; } ComponentRegistrationDataBuilder& addAlias(const std::string& alias) @@ -117,10 +120,9 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder return *this; } - template - ComponentRegistrationDataBuilder& withDeductionRule() + ComponentRegistrationDataBuilder& withDeductionRule(const std::shared_ptr& rule) { - data->templateDeductionRule = std::make_shared(); + data->templateDeductionRule = rule; return *this; } @@ -130,6 +132,18 @@ struct SOFA_CORE_API ComponentRegistrationDataBuilder return *this; } + ComponentRegistrationDataBuilder& withClass(const BaseClass* classData) + { + data->classData = classData; + return *this; + } + + ComponentRegistrationDataBuilder& withCreator(std::unique_ptr creator) + { + data->creator = std::move(creator); + return *this; + } + operator ComponentRegistrationData::SPtr() const { return data; @@ -152,52 +166,39 @@ inline std::ostream& operator<<(std::ostream& os, const ComponentRegistrationDat return os; } -struct SOFA_CORE_API ComponentRegistrationDataModule -{ - ComponentRegistrationDataBuilder withDescription(const std::string& description) - { - return {m_componentName, m_moduleName, description, std::move(m_creator), m_classData}; - } - ComponentRegistrationDataModule(const std::string& componentName, const std::string& moduleName, std::unique_ptr creator, const BaseClass* classData) - : m_componentName(componentName), m_moduleName(moduleName), m_creator(std::move(creator)), m_classData(classData) {} -private: - std::string m_componentName; - std::string m_moduleName; - std::unique_ptr m_creator; - const BaseClass* m_classData; +template +concept HasTemplateDeductionRule = requires { + typename T::TemplateDeductionRule; }; -struct SOFA_CORE_API ComponentRegistrationDataName +template +ComponentRegistrationDataBuilder CreateComponent(const std::string& componentName) { - ComponentRegistrationDataModule withModule(const std::string& moduleName) + std::unique_ptr creator = std::make_unique>(); + BaseClass* classData = Component::GetClass(); + + std::shared_ptr templateDeductionRule { nullptr }; + if constexpr (HasTemplateDeductionRule) { - return {m_componentName, moduleName, std::move(m_creator), m_classData}; + templateDeductionRule = std::make_shared(); + } + else + { + templateDeductionRule = std::make_shared>(); } - ComponentRegistrationDataName(const std::string& componentName, std::unique_ptr creator, const BaseClass* classData) - : m_componentName(componentName) - , m_creator(std::move(creator)) - , m_classData(classData) - {} -private: - std::string m_componentName; - std::unique_ptr m_creator; - const BaseClass* m_classData; -}; -template -ComponentRegistrationDataName CreateComponent(const std::string& componentName) -{ - std::unique_ptr creator = std::make_unique>(); - auto* classData = Component::GetClass(); - return ComponentRegistrationDataName(componentName, std::move(creator), classData); + return ComponentRegistrationDataBuilder() + .withName(componentName) + .withClass(classData) + .withDeductionRule(templateDeductionRule) + .withCreator(std::move(creator)); } template -ComponentRegistrationDataName CreateComponent() +ComponentRegistrationDataBuilder CreateComponent() { - std::unique_ptr creator = std::make_unique>(); BaseClass* classData = Component::GetClass(); - return ComponentRegistrationDataName(classData->className, std::move(creator), classData); + return CreateComponent(classData->className); } diff --git a/Sofa/framework/Core/src/sofa/core/behavior/ForceField.h b/Sofa/framework/Core/src/sofa/core/behavior/ForceField.h index 91e87c4abeb..ce8230d50dd 100644 --- a/Sofa/framework/Core/src/sofa/core/behavior/ForceField.h +++ b/Sofa/framework/Core/src/sofa/core/behavior/ForceField.h @@ -55,6 +55,9 @@ class ForceField : public BaseForceField, public virtual SingleStateAccessor DataVecCoord; typedef core::objectmodel::Data DataVecDeriv; + + using TemplateDeductionRule = core::MechanicalStateDeductionRule; + protected: explicit ForceField(MechanicalState *mm = nullptr); From 196594af33d0edd8bab15662dcc7c4efe5bba776 Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Thu, 2 Jul 2026 14:47:44 +0200 Subject: [PATCH 71/71] experiment --- .../fem/elastic/BeamFEMForceField.cpp | 1 - .../src/sofa/core/ComponentRegistrationData.h | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp index b7f21ba5b69..613200e9f78 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BeamFEMForceField.cpp @@ -36,7 +36,6 @@ void registerBeamFEMForceField(sofa::core::ObjectFactory* factory) core::CreateComponent>() .withModule(MODULE_NAME) .withDescription("Beam finite elements.") - .template addTemplateAttribute("dofType") ); } diff --git a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h index 6981bbcd2f8..f144f112918 100644 --- a/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h +++ b/Sofa/framework/Core/src/sofa/core/ComponentRegistrationData.h @@ -174,9 +174,6 @@ concept HasTemplateDeductionRule = requires { template ComponentRegistrationDataBuilder CreateComponent(const std::string& componentName) { - std::unique_ptr creator = std::make_unique>(); - BaseClass* classData = Component::GetClass(); - std::shared_ptr templateDeductionRule { nullptr }; if constexpr (HasTemplateDeductionRule) { @@ -187,11 +184,20 @@ ComponentRegistrationDataBuilder CreateComponent(const std::string& componentNam templateDeductionRule = std::make_shared>(); } - return ComponentRegistrationDataBuilder() - .withName(componentName) - .withClass(classData) - .withDeductionRule(templateDeductionRule) - .withCreator(std::move(creator)); + auto builder = ComponentRegistrationDataBuilder() + .withName(componentName) + .withClass(Component::GetClass()) + .withDeductionRule(templateDeductionRule) + .withCreator(std::make_unique>()); + + if constexpr (requires {typename Component::DataTypes;}) + { + return builder.addTemplateAttribute("dofType", typename Component::DataTypes::Name()); + } + else + { + return builder; + } } template @@ -202,7 +208,6 @@ ComponentRegistrationDataBuilder CreateComponent() } - /**************************************************************************************************/