SEARCH
TOOLBOX
LANGUAGES
FirstSteps

FirstSteps

From SOFAWiki

Jump to: navigation, search
First Steps with SOFA

SOFA users and developers, feel free to contribute to this page, it is designed to help new comers to quickly have a global vision of SOFA, and not feel completely lost! Thanks Marie for the first draft !

Contents

What is SOFA

SOFA is an Open Source framework primarily targeted at real-time simulation, with an emphasis on medical simulation. It is mostly intended for the research community to help develop newer algorithms, but can also be used as an efficient prototyping tool. Based on an advanced software architecture, it allows to:

  • create complex and evolving simulations by combining new algorithms with algorithms already included in SOFA
  • modify most parameters of the simulation – deformable behavior, surface representation, solver, constraints, collision algorithm, etc. – by simply editing an XML file
  • build complex models from simpler ones using a scene-graph description
  • efficiently simulate the dynamics of interacting objects using abstract equation solvers
  • reuse and easily compare a variety of available methods

SOFA is currently developed by 3 INRIA teams: Alcove, Evasion and Asclepios but the project also benefited from the help of the CIMIT Sim Group, ETH Zurich and CSIRO.

How to get SOFA

In this page, Download you can find information about how to get the latest version of SOFA.

Setting up the project

To configure SOFA, we have mainly two files located in the main directory, sofa.cfg, and sofa-default.cfg. It is highly recommended to not modify sofa.cfg unless you are really aware of what you are doing. sofa-default.cfg contains all the options you might need to configure SOFA. Sofa users usually create a copy of this file, and name it sofa-local.cfg. You can comment/uncomment the options you want. A program using Qt exists to help you in this task, sofaConfiguration.

sofaConfiguration

You can find sofaConfiguration in $SOFA/applications/projects/sofaConfiguration
This project does not depend on any sofa library, so before even thinking about compiling SOFA, just go in the directory $SOFA/applications/projects/sofaConfiguration and execute:

qmake
make

Then execute the binary $SOFA/bin/sofaConfiguration


Image:SofaConfiguration.jpg


The main advantage of using sofaConfiguration, instead of manually modifying the sofa-local.cfg or sofa-default.cfg, is that it automatically touches all the files in SOFA needed to take the options selected into account; this way, you won't have to clean all the project, but just recompile the modules in SOFA depending on these options.

Installing SOFA

An Installation guide can be found here with all the dependencies needed.

Understanding SOFA

Documentation

The main concepts of SOFA are explained in http://www.sofa-framework.org/sofa/doc/sofadocumentation.pdf

Organization

The SOFA sources are split into several directories, each of them corresponding to a concept, and has a specific goal. The main directories are:

Resources
  • doc : SOFA Manual
  • examples : Scene files provided by SOFA
    • Demos : complex scenario, using combination of lots of SOFA components; for advanced use of SOFA
    • Components : examples files to test the behaviour of almost all the Sofa components
    • SandBox : we put here the testing scenes for in-development or unstable components
    • Objects : combination of objects, to be added to the scenes: dynamic topologies, preset for the Modeler, ...
    • CUDA : scenes using SOFA CUDA
    • Tutorials : scenes described at https://wiki.sofa-framework.org/wiki/Tutorials
  • extlibs : all the external libraries used by SOFA. To use them, most of the time, you have to activate an option for SOFA. To do so, you can use sofaConfiguration, or modify by hand sofa-default.cfg or the equivalent sofa-local.cfg, and touch the depending files. For both option, you have to compile SOFA afterwards
  • share : main file repository of SOFA, it contains the textures, 3d models, icons, ... used by SOFA
Sources
  • framework : WARNING you should not modify any files in this directory, this is the core of SOFA where all the base classes, and types are defined
    • sofa
      • core : principal concepts used in SOFA: it contains the base class: ForceField, Mapping, State, ... You should take a look to see the API provided by SOFA, but not modify them
      • defaulttype : types used in SOFA, from the Rigid types, Mass Types, ...
      • helper : facility classes like Quaternions, Factory, OpenGL functions, Input-Output system, FileRepository handler
  • modules : where the SOFA Component are actually coded
    • sofa
      • component : Sofa Components subdivided into categories (Mapping, ForceField, VisualModel ...)
      • simulation : how the scene graph and simulation is handled
      • gpu : gpu version of some SOFA components:


Tutorials

We created some basic scenes, and progressive examples to show how SOFA works. You can find them here https://wiki.sofa-framework.org/wiki/Tutorials.

C++ Tutorials

We made 4 little projects coded in C++ to show how you can create your own scene using SOFA: $SOFA/applications/tutorials/

  • oneParticule: a particle with a mass under gravity
  • oneTetrahedron: a tetrahedron with a topology under gravity. We added two main features: a fixed constraint, and a visual model mapped to the simulated Tetrahedron
  • mixedPendulum: two mechanical objects of different nature linked together by a spring.
  • chainHybrid: a complex scene, the same as the one written in xml, available in $SOFA/examples/Demos/chainHybrid.scn. Several objects, of different nature, using complex topologies, visual and collision models, mixed together, and using collision detection and response.

Examples

We try to respect one rule in Sofa: for each component we provide a .scn file, in order to show how it can be used. This way we built the hierarchy of directories in $SOFA/examples/Components: in each subdirectory, forcefield, mapping, ... you will find a scene with the same name as the component. Don't hesitate to spend some times, experimenting the different components. There may be already one ready to do what you are thinking of.

Creating Scenes

XML format

To create a data structure and link the different components (C++ module of SOFA) together, scene graphs (simulation tree in XML format) are used to describe a simulation. In term of simulation tree, nodes are used to create hierarchical levels of modeling (e.g. behavior model, collision model, visual model, etc.), whereas the large collection of components corresponds to leaves under the nodes and covers a large set of fields (e.g. mechanical behavior, force fields, topology, mass, etc. ).

This description using a XML file allows a high flexibility in designing a simulation scene. Indeed, it is easy to replace one simple component by another. For example for the computation of forces acting on a mechanical system, one can replace a spring-mass force with a finite element force. Changing a component does not affect other components in the scene due to the library architecture.


Simple example of XML scene graph:

Image:pendulum.png

In this simple example, a whole pipeline allowing to simulate a pendulum is described. Each component (white name) is a C++ class of SOFA with parameters (explain in next paragraph). Thus, in this example, each component has a different goal. - The solver system with a ODE scheme for each time step and a linear solver to solve integrated time step. - The mechanical object to define the geometry of simulated object and play the role of mechanical position container. - Then, FixedConstraint and the mass define the rest of the mechanical model. - Finaly the spring forcefield, define the spring.


Data input-output

All parameters describing a scene are stored in specific C++ containers called "Data". Those containers are C++ components members reachable in the XML file. If we take the previous example of the pendulum. In each component, we can see parameter (in blue). Those parameters are specific containers member of the different c++ classes. For example the "Data name", which define simply the name of the component, is a container Data <string>. In SOFA there are common Data, i.e which can be access in every component. Those Data are: TODO chercher la liste. Then specific Data, like for example "indices" of FixedConstraint, which define the index of the point to be fixed, should be declare in this specific C++ class.


Moreover, dependency between Data containers of same nature can be specified in the XML scene files, indicating that the content of one container should be copied from the other, making the scene description fairly simple and above all efficient. To create this dependency, the flag "@" follow by the name of the source component should be use. Like in next example. Note that it is possible to link to a component located in an other node. Thus, the syntax: myData = "@LinkToOtherNode/SourceComponent.myData". Where LinkToOtherNode = ../ if you should go up to one node, or directly the name of a node. TODO: ajouter un example, ça sera plus clair.

Image:dataLink.png


Including Files

How to use the <include href="..."/> command, and overriding some parameters

Loading Mesh Files

Mesh files located in $SOFA_DIR/share or $SOFA_DIR/examples can be automatically found by Sofa DataRepository system, so you can write <MeshLoader filename="mesh/liver.msh" />.

Using Php+XML

Explain how to generate a scene using php+xml

Modeler

A graphic tool exists in SOFA to ease the creation of new scenes, the Modeler. You can find a description at this page : https://wiki.sofa-framework.org/wiki/Modeler

To see a video explaining how to use it and some of the basic operations you can do with the Modeler, click on that link: http://www.youtube.com/watch?v=IFzNY45z2lY

Writing C++ scenes

Creating a new program

TODO Explain how to pass parameters to the program: runSofa -r; //runs Sofa using the latest scene


Typedefs

For new comers, writing C++ scenes can be a difficult task; in Sofa, we made an intensive use of templates to achieve a good genericity and modularity. For the user, the side effect is a code more difficult to read, and the almost compulsory need to use typedef. To help people to write C++ scenes, we made a tool to generate typedef files. They are placed within the directory: $SOFA/modules/sofa/component/typedef.

You just need to add the header

#include <sofa/component/typedef/Sofa_typedef.h>

And then all the typedef for all the Sofa components using template will be available. For instance, to create a mechanical object, instead of writing:

sofa::component::container::MechanicalObject<sofa::defaulttype::StdVectorTypes<sofa::defaulttype::Vec<3, double>, sofa::defaulttype::Vec<3, double>, double> > *mstate;

you just need to write:

MechanicalObject3d *mstate;

The rules are simple: the typedef consists in the name of the component + prefix corresponding to its data type.

Prefix Generation
sofa::defaulttype::StdVectorTypes<sofa::defaulttype::Vec<3, double>, sofa::defaulttype::Vec<3, double>, double> 3d
sofa::defaulttype::StdVectorTypes<sofa::defaulttype::Vec<2, float>, sofa::defaulttype::Vec<2, float>, float> 1f
sofa::defaulttype::StdRigidTypes<3, double> Rigid3d
sofa::defaulttype::ExtVectorTypes<sofa::defaulttype::Vec<3, float>, sofa::defaulttype::Vec<3, float>, float> > > Ext3f

For instance, if you want to use the component ConstantForceField:

ConstantForceField3d *constant3d; //force field using particules in 3 dimension, using double precision
ConstantForceFieldRigid3f *constantRigid3f; //forcefield using rigid frames in 3 dimension, using simple precision

The typedef for Mappings are slightly different as they make the link between two objects of somewhat different datatypes. The BarycentricMapping is one of the most used mapping in Sofa scenes. To create a barycentric mechanical mapping in C++, you have to write:

sofa::component::mapping::BarycentricMapping<sofa::core::componentmodel::behavior::MechanicalMapping<sofa::core::componentmodel::behavior::MechanicalState<sofa::defaulttype::StdVectorTypes<sofa::defaulttype::Vec<3, double>, sofa::defaulttype::Vec<3, double>, double> >, sofa::core::componentmodel::behavior::MechanicalState<sofa::defaulttype::StdVectorTypes<sofa::defaulttype::Vec<3, double>, sofa::defaulttype::Vec<3, double>, double> > > > *mapping(mstate1,mstate2);

Using our typedef, it gives:

BarycentricMechanicalMapping3d_to_3d *mapping(mstate1,mstate2);

The rules to find the typedef for the mappings are : name of the mapping + prefix for Object1 + "_to_"prefix for Mapped Object2 See the table above to get the prefix. If the mapping is mechanical (it propagates the velocities from the DOFs to the mapped state, and the forces from the mapped state to the DOFs), you have to write MechanicalMapping. If the mapping is done just in position, like a VisualMapping, you have to write Mapping.

BarycentricMapping3d_to_Ext3f *baryMapping(mstate,visualmodel); //Visual Mapping: often used to add a visual model to a deformable simulated body
RigidMechanicalMappingRigid3d_to_3d *rigidMapping(rigidState, mappedState); //Mechanical Mapping: often used to add a collision model to a rigid simulated body

Discover SOFA Mechanisms

SOFA relies intensively on the concept of Visitors. Animating a Simulation consists in launching a sequence of Visitors, each of them acting on a subset of Sofa Components, and triggering some specific operations. Now, if you manage to decode the visitor execution, you will get a global vision of the execution of one step of simulation in SOFA.
A tool has been created to do so, it helps you to trace the execution of the Visitor: https://wiki.sofa-framework.org/wiki/Tips#Trace_and_Profiling


Image:Trace.png


In SOFA, click on the option Trace Visitor and Component Execution of the Stats tabulation. Then, proceed to one step of simulation. Now you can see the sequence of calls, the nodes traversed and components used. You can even trace the evolution of the state vector with our latest version of the tools.

Developing new components

Using plugins

To create a new components for SOFA, it is highly recommended to use a plug-in: here is a documentation to do so https://wiki.sofa-framework.org/wiki/PluginCreation This way, you will be able to specify you own license, and distribute it easily.

New Components

Here, https://wiki.sofa-framework.org/wiki/ComponentCreation several, we list several hints about how to create a new SOFA component.


Component Configuration using Data

In SOFA, we use the concept of Data to configure a component: all the parameters to specify the options, or an initial configuration must be set using an object of the class core::objectmodel::Data. A Data handles automatically the import of the configuration of your component from a scene file, and the export of its current state, if you decide to save the scene. BRThis is the reason why, EVERYTHING that have to be saved, or loaded MUST be put inside a Data.


Input/Output stream

A Data is template with the type of your option: a boolean, an integer, ... But it is not restricted to the default types, you put inside a Data your own class, or data structure. Your class must implement the operator << and >> in order to be used through an input and output stream:

struct MyStruct
{
    bool active;  
    int  value;
 
    inline friend std::istream& operator >> ( std::istream& in, MyStruct& s ){
        in >> s.active >> s.value
            return in;
    }
 
    inline friend std::ostream& operator << ( std::ostream& out, const MyStruct& s ){
        out << s.active << " " << s.value;
        return out;
    }
};
Data<MyStruct> configureStruct;

Data Container

To ease the use of Data, we already serialized some of the most common data container: map, vector, set, list. Instead of using a std::vector, use a helper::vector:

Data< helper::vector<int> > values;

WARNING: to use a vector< vector< int> >, you must use a SVector instead:

Data< helper::SVector< helper::vector< int> > > vecValues;


Example of Use

For instance, to add a boolean in your component to activate a specific behaviour A, you have to:

  1. declare a Data template with the type of your option: here, a boolean
    • Data<bool> activeOption;
  2. in the constructor of you class MyClass, initialize the Data
    • MyClass(): activeOption(initData(&activeOption, true, "enableOptionA", "Activate the option A")){};
    • initData takes several parameters, some are optionals:
      1. &activeOption [REQUIRED]: the pointer to the data.
      2. true [OPTIONAL]: a default value. When the option is not specified (in xml or C++), the Data will have this value.
      3. "enableOptionA" [REQUIRED]: the name of your option, as it will appear in the XML file.
<Node name="Root">
   <MyClass enableOptionA="true"/>
</Node>

How to Manipulate a Data

Read-only Access

To have a read only access to the data, use the method getValue()

//with Data<bool> activeOption; 
//and  Data<helper::vector< int > > values;
void MyClass::doOperation()
{
  bool isActive=activeOption.getValue();
  const helper::vector< int > &v=values.getValue();
  if (isActive)
  {
    for (unsigned int i=0;i<v.size();++i) ;
  }
  //...
}

Write Access

To change the value of the data, use the method setValue(...)

//with Data<bool> activeOption; 
//and  Data<helper::vector< int > > values;
void MyClass::changeParameters(bool b)
{
  activeOption.setValue(b);
  helper::vector< int > newVector(10);
  values.setValue(newVector);
  //...
}


Read/Write Access

If you want to have a direct access to the data, and want to manipulate it, use the commands beginEdit() and endEdit

  • beginEdit() return a pointer to the internal data, and specify that the Data is currently modified.
  • endEdit() to announce the end of the modification, and the eventual propagation of the new value.

This is commonly used while manipulating vectors of data.

//with Data<bool> activeOption; 
//and  Data<helper::vector< int > > values;
void MyClass::manipulatingParameters()
{
  helper::vector<int> *myValues=values.beginEdit();
  //Modifying the index 0 of the vector
  (*myValues)[0] = 2;
  values.endEdit();
}

Basic knowledges about Sofa Components

A Sofa component is a class deriving from sofa::core::objectmodel::BaseObject. This way, several virtual methods are provided, and must be known in order to configure correctly the behavior of your component:

init() and bwdInit()

When SOFA loads a simulation, its creates in C++, or directly using XML the Sofa Components (and the default constructor). At this stage, you must initialize what we call Data, a component that you can find in sofa::core::objectmodel::Data. The purpose of this utility class is to store all the parameters of your component, and handle this way the input (parametrize the component from xml files for instance), and output (save at a time T the configuration of your component). Everything that needs to be saved in your component must be kept into memory inside a Data. Basically to initialize a data, you must do the following:


//Previous declaration of the Data
Data<bool> isEnabled;
//In Component constructor
MyComponent():
isEnabled(initData(&isEnabled, true, "isEnabled", "Boolean indicating if the component is enable"))) //ptr to the data, default value, name used for the parameter (the same that will appear later in the XML file), description of the parameter (its purpose)
{
};


Once all the Sofa Components of the scenes have been created, we launch a Visitor to initialize the components: Basically a Visitor starts from a node (for the InitVisitor, we start from the root), execute several specific operations going top->down, and then another set of operations going bottom->up. This is translated for the InitVisitor by the call, each time we initialize a scene of two methods

void init(); //call during Top->Down traversal
void bwdInit(); //call during Bottom->Up traversal

bwdInit is called once all the children of a node has been initialized, the methods init() and bwdInit() have been called for all the component of the children nodes.

reinit()

The purpose of the reinit() method is to recompute, and reconfigure your component when you have modified one or several of its Data. Typically, we automatically call reinit() when you edit a component in the GUI of Sofa.

void reinit();

cleanup() and reset()

void cleanup();
voir reset();

These methods are called each time you want to reset a scene: first cleanup will be called, then reset.

* In cleanup, you have to remove all the components you might have added to the scene: if in the scene, you have some collisions, and you create contact components, or collision response components, cleanup is a good place for you to remove them. 
* In reset, you must set back to default all the Datas and internal values of your component.


draw()

void draw();

All Sofa components have a method draw(), so you don't need to derive from a VisualModel base component to display debug information!. It is called at the end of the simulation time, directly by the GUI. At this moment, we only have one thread running both the simulation and the visualization. Soon we want to separate these processes into two different thread so that the frequency of the visualization doesn't depend anymore on the frequency of the simulation (generally much slower).


getContext()

sofa::core::objectmodel::BaseContext* getContext();

All Sofa component has a context. By casting this context to a simulation::Node*, you manage to get the node containing your component. A Node is a very useful component, as you can launch visitors from them, or quickly get information about the content of the node.

Exampe of use:

simulation::Node* currentNode = static_cast<simulation::Node*>(myComponent->getContext());


handleEvent( Event* )

void handleEvent( Event* );

If the Data f_listening is true, then each time an Event is sent to the node containing your component, this method will be called. This way, you can execute specific operations when an event is triggered.

void handleEvent ( core::objectmodel::Event* ev )
{
  if ( dynamic_cast<sofa::simulation::CollisionEndEvent *> ( ev ) )
  {
    // Do some operation when the collision detection ends.
  }
}

You can use the Trace of Visitor to know when and where the Events are triggered.

The most common Sofa events are:

  • AnimateBeginEvent
  • AnimateEndEvent
  • CollisionBeginEvent
  • CollisionEndEvent
  • TopologyChangeEvent
  • UpdateMappingEndEvent


Most common operations

When you use a Component in Sofa, you generally make it interact with the rest of the scene, and more specifically the node containing your component and the hierarchy above and below it.

Get

One basic need you will have will be to get pointers to some components: Get the Mechanical State, or the Mapping...

  • get One component:
    • In you component, you want to get, or verify the presence of a MechanicalMapping
core::componentmodel::behavior::BaseMechanicalMapping* mapping;
this->getContext()->get(mapping);
if (mapping)
{  //Mechanical Mapping found
}
else
{  //Mechanical Mapping NOT found
}

By default this will search inside the node containing you component, and if nothing is found, goes up in the hierarchy of node. Other directions can be specified

//Same call than this->getContext()->get(mapping);
this->getContext()->get(mapping, sofa::core::objectmodel::BaseContext::SearchUp);
//Starts from local node, then if nothing is found, goes down in the hierarchy of node
this->getContext()->get(mapping, sofa::core::objectmodel::BaseContext::SearchDown);
//Search only inside the local node
this->getContext()->get(mapping, sofa::core::objectmodel::BaseContext::Local);
//Search from the Root node, then goes down to all the nodes
this->getContext()->get(mapping, sofa::core::objectmodel::BaseContext::SearchRoot);
  • get all components of a given type

You may want to get not only one but all the CollisionModels in a given direction.

sofa::helper::vector< sofa::core::CollisionModel* > list_collisionModels; //container for the component you want to find
//Default call: search from current node, then goes up
static_cast<simulation::Node*>(this->getContext())->get< sofa::core::CollisionModel >( &list_collisionModels );
//Same behavior as previous call, only it explicits the method called
static_cast<simulation::Node*>(this->getContext())->get< sofa::core::CollisionModel >( &list_collisionModels, BaseContext::SearchUp);
//Starts from the current node, and goes down
static_cast<simulation::Node*>(this->getContext())->get< sofa::core::CollisionModel >( &list_collisionModels, BaseContext::SearchDown);
//Search only in the current node
static_cast<simulation::Node*>(this->getContext())->get< sofa::core::CollisionModel >( &list_collisionModels, BaseContext::Local);
//Search from the root
static_cast<simulation::Node*>(this->getContext())->get< sofa::core::CollisionModel >( &list_collisionModels, BaseContext::SearchRoot);

For all these calls, you can add a third parameter, for advanced searches. You can retrieve only components containing a set of Tags, or the same tags as your component:

//Find a mapping (previously declared), containing the same set of tags as your component
this->getContext()->get(mapping, this->getTags(), sofa::core::objectmodel::BaseContext::SearchDown);
 
//Find a mapping (previously declared), containing the set of tags "Fluid" and "Mecha"
sofa::core::objectmodel::TagSet tagsToFind;
tagsToFind.insert(sofa::core::objectmodel::Tag("Fluid"));
tagsToFind.insert(sofa::core::objectmodel::Tag("Mecha"));
this->getContext()->get(mapping, tagsToFind, sofa::core::objectmodel::BaseContext::SearchDown);

Launch Visitor

The genericity of Sofa is achieved mainly by the use of Visitors. It is good to know how to launch them.

simulation::Node *currentNode=static_cast<simulation::Node*>(this->getContext());
currentNode->execute<simulation::VisualUpdateVisitor>(); //Launch the visitor VisualUpdateVisitor from the currentNode
 
//If you need to configure your Visitor before launching it
sofa::simulation::MechanicalWriteLMConstraint  LMConstraintVisitor;
LMConstraintVisitor.setOrder(orderState); //configuring the Visitor
//...
//Launch the Visitor
LMConstraintVisitor.execute(this->getContext());

As mentioned previously, you can use Tags in order to execute your Visitor only on the components containing a set of Tags

LMConstraintVisitor.setTags(this->getTags()).execute(this->getContext());

Object Factory

To support the creation of a scene from a xml description, we rely on what we call an ObjectFactory. Without going into all the details, you have to know what is the purpose of these methods:

canCreate

 template<class T>
    static bool canCreate(T*& obj, core::objectmodel::BaseContext* context, core::objectmodel::BaseObjectDescription* arg);

This method is called before the effective creation of the object specified in the xml: this way, you have access to the current context (object that have already been created), and you can process to some basic verifications; for instance, if your component needs a specific template, or another component to work, you can test this here.

create

template<class T>
    static void create(T*& obj, BaseContext* context, BaseObjectDescription* arg);

If, and if only, the method canCreate answered true, the component is created: By default, the implementation made in sofa::core::objectmodel::BaseObject does:

template<class T>
    static void create(T*& obj, BaseContext* context, BaseObjectDescription* arg)
{
obj = new T; 
if (context) context->addObject(obj); 
if (arg) obj->parse(arg); 
}

Three steps:

  • Creation of the object passed by template (look at how works the design pattern of an abstract factory)
  • Add the object inside the current context: the side effect of this instruction is that the new object will appear in the scene graph inside the node corresponding to the context
  • parse method to initialize the parameters described in XML. All the parameters stored as Data will be automatically set, YOU DON'T HAVE TO IMPLEMENT YOUR OWN PARSE METHOD!

templateName

std::string getTemplateName() const;
static std::string templateName(const MyTemplateClass<DataTypes>* = NULL);

These methods are used to read/write your component name, if it contains a template type. Instead of having something not user-friendly like

<MyTemplateClass template="StdVectorTypes&lt;Vec&lt;3, double&gt;, Vec&lt;3, double&gt;, double&gt; &gt;"/>

To improve this, simply implements these two methods: for instance, using this

std::string getTemplateName() const
{
   return templateName(this);
}
static std::string templateName(const MyTemplateClass<DataTypes>* = NULL)
{
   return DataTypes::Name();
}

your component will be written:

<MyTemplateClass template="Vec3d" />

General C++ tips

.inl files

Often the question is asked about what the inl files are, and what they are useful for
If you have no experience at all in template programming, I suggest you take a look at the "Useful link" section, where you can find good introductions to that area.
Historically .inl files are used to implement the definition of inline functions, so that to keep a "clean" .h with only declarations. In the same way, for template classes, the .inl files contains the definitions of the member functions of the class. The .cpp file is used to specify some instanciations of the template class, with sensible template parameter types.
Here is an example :

in the .h header file

template< typename MyType >
class MyTemplateClass
{
  void SomeMethod();
};

in the .inl header file resides definitions for the class member functions.

#include "MyTemplateClass.h"
 
template< typename MyType>
void MyTemplateClass<MyType>::SomeMethod()
{
/*
some stuff happens.
*/
}

the .cpp provides some instanciations of the class, with sensible template parameters.

/*
at this point the compiler needs to "see" both the declarations and definitions of 
MyTemplateClass members in order to generate the code for specific template 
parameters, therefore we include the .inl file
*/
#include "MyTemplateClass.inl"  
 
template class MyTemplateClass<char>;
template class MyTemplateClass<unsigned int>;
template class MytemplateClass<int>;


Useful links