Skip to content

The ObjectFactory

The ObjectFactory

The ObjectFactory is mostly a register which gives a correspondence between a component name and a function pointer to a method able to construct that object. It is located in the Sofa.Core library.

Registering a component

In SOFA a component is a class which has sofa::core::objectmodel::BaseObject in its class inheritance hierarchy. In order to make your component available in SOFA you have to call the registration method of the ObjectFactory, and use two macros SOFA_DECL_CLASS and SOFA_LINK_CLASS whose purpose is to make sure that the code which actually registers the component in the factory is called in the output binary. The summary of the steps to follow is here :

  • Add a : SOFA_DECL_CLASS(NewComponent) in your .cpp file, NewComponent being the class name of your component.
  • Register the component: it is generally done in the .cpp of your new component class. If your component is not templated:

    #include <sofa/core/ObjectFactory.h>
     int NewComponentClass = sofa::core::RegisterObject("Description of your component")
    .add< NewComponent >();
    

    if your component is templated with Vec3dTypes and Vec3fTypes (for instance)

     int NewComponentClass = sofa::core::RegisterObject("Description of your component")
    .add< NewComponent<Vec3dTypes> >()
    .add< NewComponent<Vec3fTypes> >()
    ;
    
  • Add a : SOFA_LINK_CLASS(NewComponent) in the init file.

    • If you chose to put your components directly inside SOFA modules directories you will find a file called initNameCategoryComponent.cpp with NameCategoryComponent being the category of your component: ForceField, Constraint, Mapping ... This is where you put the SOFA_LINK_CLASS macro
    • If you are writing a plugin, you have to call in the SOFA_LINK_CLASS macro from your initMyPlugin.cpp.

The documentation about the methods regarding the registration component can be found in the doxygen documentation of the sofa::core::RegisterObject class.

The ObjectFactory and the XML (MechanicalObject example)

For a given instance of SOFA we can know what are the available components by looking at the ObjectFactory entries. An entry in the ObjectFactory can point to multiple constructors, which happens to be useful when you write components which depend on a template parameter.

int MechanicalObjectClass = core::RegisterObject("mechanical state vectors")
#ifdef SOFA_FLOAT
.add< MechanicalObject >(true) // default template
#else
.add< MechanicalObject >(true) // default template
#ifndef SOFA_DOUBLE
.add< MechanicalObject >()
#endif
#endif
#ifndef SOFA_FLOAT
.add< MechanicalObject >()
.add< MechanicalObject >()
.add< MechanicalObject >()
.add< MechanicalObject >()
.add< MechanicalObject >()
#endif
#ifndef SOFA_DOUBLE
.add< MechanicalObject >()
.add< MechanicalObject >()
.add< MechanicalObject >()
.add< MechanicalObject >()
.add< MechanicalObject >()
#endif
.add< MechanicalObject >()
;

We can see that the same entry "MechanicalObject" has multiple flavor depending on the template parameter, and that the default entry points to a

  • sofa::component::container::MechanicalObject if SOFA is compiled in float mode
  • sofa::component::container::MechanicalObject if SOFA is compiled in double mode

When you write a SOFA scene in XML this means that such a line

<MechanicalObject />

will be translated by the default instance of MechanicalObject registered in the ObjectFactory. If you want a specific flavor of MechanicalObject, you have to specify it by using the template attribute in the XML.

<MechanicalObject template="Vec3d"/>

This will produce a MechanicalObject templated on Vec3d, i.e. with 3 degrees of freedom per node. For a RigidType in 3D (ie Rigid3dTypes) this points to :

/// We now use template aliases so we do not break backward compatibility
template<> inline const char* Rigid3dTypes::Name() { return "Rigid3d"; }
template<> inline const char* Rigid3fTypes::Name() { return "Rigid3f"; }

The createObject method

sofa::core::objectmodel::BaseObject::SPtr obj = ObjectFactory::createObject( /* objectmodel::BaseContext* */ context, /* objectmodel::BaseObjectDescription* */arg);
MyComponent::SPtr my_obj = sofa::core::objectmodel::SPtr_dynamic_cast(obj);

The method needs two parameters :

  • core::objectmodel::BaseContext , ie the graph node where your component will reside
  • core::objectmodel::BaseObjectDescription, which is stores specific parameters of the component you are trying to create.

Example of the TetrahedronFEMForceField

sofa::core::objectmodel::BaseObjectDescription options("myFF1","TetrahedronFEMForceField");
    options.setAttribute("youngModulus", "10000");
    BaseForceField::SPtr ff = sofa::core::objectmodel::SPtr_dynamic_cast(sofa::core::ObjectFactory::CreateObject(node, &options));