From 32342a8d49570c85525c52f9eb3b2e4c0f2bb2fc Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 19 Jun 2026 21:28:50 +0200 Subject: [PATCH] [Core] Improve UX when defining template of a Component --- .../iterative/MatrixLinearSolver.h | 5 + .../statecontainer/MechanicalObject.h | 5 + .../Core/src/sofa/core/ObjectFactory.cpp | 95 +++++++++++++++---- .../Core/src/sofa/core/ObjectFactory.h | 16 +++- 4 files changed, 104 insertions(+), 17 deletions(-) diff --git a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/MatrixLinearSolver.h b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/MatrixLinearSolver.h index 66a5e5e23ea..718083072c6 100644 --- a/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/MatrixLinearSolver.h +++ b/Sofa/Component/LinearSolver/Iterative/src/sofa/component/linearsolver/iterative/MatrixLinearSolver.h @@ -226,6 +226,11 @@ class MatrixLinearSolver : public BaseMatrixLinea return ThreadManager::Name()+Matrix::Name(); } + static std::vector GetTemplateNameAttributeList() + { + return {"matrixType"}; + } + bool isAsyncSolver() override { return ThreadManager::isAsyncSolver(); diff --git a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.h b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.h index 03ae874b42e..8687f19e6ac 100644 --- a/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.h +++ b/Sofa/Component/StateContainer/src/sofa/component/statecontainer/MechanicalObject.h @@ -71,6 +71,11 @@ class MechanicalObject : public sofa::core::behavior::MechanicalState typedef typename DataTypes::MatrixDeriv::RowIterator MatrixDerivRowIterator; typedef typename core::behavior::BaseMechanicalState::ConstraintBlock ConstraintBlock; + static std::vector GetTemplateNameAttributeList() + { + return {"dofType"}; + } + protected: MechanicalObject(); public: diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp b/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp index 24947674ec5..2a845848b14 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactory.cpp @@ -138,6 +138,69 @@ void findTemplatedCreator( } } +std::vector replaceDeprecatedTemplates(std::vector& templateNames) +{ + std::vector deprecatedTemplates; + for(auto& name : templateNames) + { + const sofa::defaulttype::TemplateAlias* alias = sofa::defaulttype::TemplateAliases::getTemplateAlias(name); + if( alias != 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; + } + } + return deprecatedTemplates; +} + + +std::optional buildTemplateNamesFromAttributes(const ObjectFactory::ClassEntry& entry, objectmodel::BaseObjectDescription* arg) +{ + assert(arg); + if (entry.templateNameAttributes.has_value()) + { + const auto& attributeList = entry.templateNameAttributes.value(); + if (attributeList.empty()) + return {}; + + std::vector attributeValues; + + std::vector foundAttributes; + std::vector missingAttributes; + + for (const auto& attributeName : attributeList) + { + if (auto* attr = arg->getAttribute(attributeName, nullptr)) + { + foundAttributes.push_back(attributeName); + attributeValues.emplace_back(attr); + } + else + { + missingAttributes.push_back(attributeName); + } + } + + if (!attributeValues.empty() && !missingAttributes.empty()) + { + arg->logError("Found attributes '" + sofa::helper::join(missingAttributes, ", ") + + "', but not '" + sofa::helper::join(foundAttributes, ", ") + + "', preventing to deduce a template list"); + } + + replaceDeprecatedTemplates(attributeValues); + return sofa::helper::join(attributeValues, ","); + } + + return {}; +} + objectmodel::BaseComponent::SPtr ObjectFactory::createObject(objectmodel::BaseContext* context, objectmodel::BaseObjectDescription* arg) { objectmodel::BaseComponent::SPtr object = nullptr; @@ -157,22 +220,8 @@ objectmodel::BaseComponent::SPtr ObjectFactory::createObject(objectmodel::BaseCo /// 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+"."); - } + std::vector deprecatedTemplates = replaceDeprecatedTemplates(usertemplatenames); - name = alias->first; - } - } std::string templatename = sofa::helper::join(usertemplatenames, ","); std::string userresolved = templatename; // Copy in case we change for the default one //////////////////////////////////////////////////////////////////////////////////////////////// @@ -194,11 +243,24 @@ objectmodel::BaseComponent::SPtr ObjectFactory::createObject(objectmodel::BaseCo 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 (templatename.empty()) + { + const auto templateNameFromAttributes = buildTemplateNamesFromAttributes(*entry, arg); + if (templateNameFromAttributes.has_value()) + { + const auto& templateNameFromAttributesValue = templateNameFromAttributes.value(); + if (entry->creatorMap.contains(templateNameFromAttributesValue)) + { + templatename = templateNameFromAttributesValue; + } + } + } + // 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; @@ -714,6 +776,7 @@ bool ObjectRegistrationData::commitTo(sofa::core::ObjectFactory* objectFactory) reg.authors += entry.authors; reg.license += entry.license; reg.documentationURL += entry.documentationURL; + reg.templateNameAttributes = entry.templateNameAttributes; if (!entry.defaultTemplate.empty()) { if (!reg.defaultTemplate.empty()) diff --git a/Sofa/framework/Core/src/sofa/core/ObjectFactory.h b/Sofa/framework/Core/src/sofa/core/ObjectFactory.h index d297f11a72f..11add095b45 100644 --- a/Sofa/framework/Core/src/sofa/core/ObjectFactory.h +++ b/Sofa/framework/Core/src/sofa/core/ObjectFactory.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace sofa::helper::system @@ -110,7 +111,10 @@ class SOFA_CORE_API ObjectFactory std::string documentationURL; std::string defaultTemplate; ObjectTemplateCreatorMap creatorMap; // to create instances of the class for different templates - std::map> m_dataAlias ; + std::map> m_dataAlias; + + // list of keys. + std::optional> templateNameAttributes; }; using ClassName = std::string; @@ -363,6 +367,11 @@ class SOFA_CORE_API ObjectRegistrationData const std::string classname = sofa::core::objectmodel::BaseClassNameHelper::getClassName(); const std::string templatename = sofa::core::objectmodel::BaseClassNameHelper::getTemplateName(); + if constexpr (requires {RealObject::GetTemplateNameAttributeList(); }) + { + entry.templateNameAttributes = RealObject::GetTemplateNameAttributeList(); + } + if (defaultTemplate) entry.defaultTemplate = templatename; @@ -388,6 +397,11 @@ class SOFA_CORE_API ObjectRegistrationData return addCreator(classname, templatename, objectCreator); } + void addTemplateNameAttributeList(const std::initializer_list& list) + { + entry.templateNameAttributes = list; + } + /// This is the final operation that will actually commit the additions to the ObjectFactory. bool commitTo(sofa::core::ObjectFactory* objectFactory) const;