jaswi |
March 23, 2009 13:39 |
Run time Selection Mechanism - Some help required to understand
Dear Forum Users
Good Evening to all
Run Time Selection Mechanism is one of the wonderful features of OpenFOAM. Understanding how it works is a bit involved. I have tried to look into its implementation but need some help from experts. I hope I will be able to get some answers.
Lets start :-)
Certain Macros constitute the core of this implementation, namely:
- declareRunTimeSelectionTable
- defineRunTimeSelectionTable
- addToRunTimeSelectionTable
Classes which use this mechanism invoke:
- the macro declareRunTimeSelectionTable in their header file
- the other two macros in their cpp file.
For further discussion , I have used the class polyPatch. If we take a look at its header file, we find it has 2 definitions for run time selection tables. They read:
Code:
// Declare run-time constructor selection tables
declareRunTimeSelectionTable
(
autoPtr,
polyPatch,
word,
(
const word& name, const label size,
const label start, const label index,
const polyBoundaryMesh& bm
),
(name, size, start, index, bm)
);
declareRunTimeSelectionTable
(
autoPtr,
polyPatch,
dictionary,
(
const word& name, const dictionary& dict,
const label index, const polyBoundaryMesh& bm
),
(name, dict, index, bm)
);
The cpp file - polyPatch.C invoke the other two macros in this way:
Macros when invoked lead to a simple string substitution and then it actually compiles. Listing just the macros will not add to the clarity of what it is doing so I have used the first definition of the selection table to rewrite what these macros are doing exactly.
macro - declareRunTimeSelectionTable:
The first macro reads (Note: syntax correctness requires forward slashes as macros cannot have line breaks) :
Code:
#define declareRunTimeSelectionTable\
(autoPtr,baseType,argNames,argList,parList)
/* Construct from argList function pointer type */
typedef autoPtr<baseType> (*argNames##ConstructorPtr)argList;
/* Construct from argList function table type */
typedef HashTable<argNames##ConstructorPtr, word, string::hash>
argNames##ConstructorTable;
/* Construct from argList function pointer table pointer */
static argNames##ConstructorTable* argNames##ConstructorTablePtr_;
/* Class to add constructor from argList to table */
template<class baseType##Type>
class add##argNames##ConstructorToTable
{
public:
static autoPtr<baseType> New argList
{
return autoPtr<baseType>(new baseType##Type parList);
}
add##argNames##ConstructorToTable
(
const word& lookup = baseType##Type::typeName
)
{
construct##argNames##ConstructorTables();
argNames##ConstructorTablePtr_->insert(lookup, New);
}
~add##argNames##ConstructorToTable()
{
destroy##argNames##ConstructorTables();
}
};
/* Table Constructor called from the table add function */
static void construct##argNames##ConstructorTables();
/* Table destructor called from the table add function destructor */
static void destroy##argNames##ConstructorTables()
This macro has five arguments:
- autoPtr - name of the pointer. In OpenFOAM its always the smart pointer autoPtr<Type > templated over the type
- baseType - Name of the base class
- argNames – Not sure what it means exactly
- argList - list of arguments
- parList - parameter list
Lets compare it with the invocation of this macro for polyPatch.
Code:
declareRunTimeSelectionTable
(
autoPtr – autoPtr
baseclass - polyPatch
argNames – word
argList - (const word& name,
const label size,
const label start,
const label index,
const polyBoundaryMesh& bm),
parList - (name, size, start, index,bm)
);
As a result invocation of this macro we will have the following code :
// it first defines a typedef for the construction of a function pointer type from argList.
Code:
typedef autoPtr<polyPatch> (* wordConstructorPtr) ( const word& name,
const label size,
const label start,
const label index,
const polyBoundaryMesh& bm
);
// Next a typedef is defined for the HashTable, given the argList. It reads:
Code:
typedef HashTable < wordConstructorPtr, word, string::hash> wordConstructorTable;
//it declares a static pointer to the constructor table.
Code:
static wordConstructorTable* wordConstructorTablePtr_;
// the template class reads :
Code:
template<class polyPatchType>
class addwordConstructorToTable
{
public:
// static function
static autoPtr<polyPatch> New ( const word& name,
const label size,
const label start,
const label index,
const polyBoundaryMesh& bm
)
{
return autoPtr<polyPatch> (new polyPatch (name, size, start, index, bm)); }
// constructor
addwordConstructorToTable ( const word& lookup = polyPatchType::typeName )
{
constructwordConstructorTables();
wordConstructorTablePtr_->insert(lookup, New);
}
// destructor
~addwordConstructorToTable()
{
destroywordConstructorTables();
}
};
Next these two static functions are declared. These two functions are used inside the template class. I am not sure about this but it seems that within a macro the order is not important. These two definitions could have been in the begining as well.
Code:
static void constructwordConstructorTables();
static void destroywordConstructorTables();
macro - defineRunTimeSelectionTable( )
Macro defineRunTimeSelectionTable(baseType, argNames) already expanded for the class polyPatch reads :
Code:
#define defineRunTimeSelectionTable( polyPatch, word )
defineRunTimeSelectionTablePtr(polyPatch, word );
defineRunTimeSelectionTableConstructor(polyPatch, word)
defineRunTimeSelectionTableDestructor(polyPatch,word)
The invocation of this macro , further invokes three macros , namely:
defineRunTimeSelectionTablePtr(polyPatch, word);
Code:
#define defineRunTimeSelectionTablePtr(polyPatch, word)
/* Define the constructor function table and initialized to NULL */
polyPatch::wordConstructorTable* polyPatch::wordConstructorTablePtr_ = NULL
defineRunTimeSelectionTableConstructor(polyPatch, word)
Code:
#define defineRunTimeSelectionTableConstructor(polyPatch, word)
/* Table Constructor called from the table add function
void polyPatch :: constructwordConstructorTables()
{
static bool constructed = false;
if (!constructed)
{
polyPatch::wordConstructorTablePtr_
= new polyPatch::wordConstructorTable;
constructed = true;
}
}
defineRunTimeSelectionTableDestructor(polyPatch, word)
Code:
#define defineRunTimeSelectionTableDestructor(polyPatch, word)
/* Table destructor called from the table add function destructor */
void polyPatch::destroywordConstructorTables()
{
if (polyPatch::wordConstructorTablePtr_)
{
delete polyPatch::wordConstructorTablePtr_;
polyPatch::wordConstructorTablePtr_ = NULL;
}
}
The first among three macros declares a pointer to the hashtable and initializes it to NULL . The 2nd and 3rd macros implement the static functions, defined in the template class addwordConstructorToTable .
macro - addToRunTimeSelectionTable( ) -
this macro is pretty simple . it reads
Code:
#define addToRunTimeSelectionTable(baseType,thisType,argNames)
/* Add the thisType constructor function to the table */
baseType::add##argNames##ConstructorToTable<thisType>
add##thisType##argNames##ConstructorTo##baseType##Table_
When invoked like this for polyPatch : addToRunTimeSelectionTable(polyPatch, polyPatch, word). it will read :
Code:
polyPatch::addwordConstructorToTable<polyPatch>
addpolyPatchwordConstructorTopolyPatchTable_
Now the questions :-))
1) How / where is the constructor addwordConstructorToTable is called to construct the table . If the code
Code:
polyPatch::addwordConstructorToTable<polyPatch>
addpolyPatchwordConstructorTopolyPatchTable_
corresponds to the call to constructor for this template class (which i doubt that it is ) then what about the ctor argument (const word& lookup = polyPatchType::typeName).
Now If I am wrong in asking the first question and we assume that ctor is indeed called then the static function call - constructwordConstructorTables() will create a new HashTable and return a pointer to it. The next statement
Code:
wordConstructorTablePtr_->insert(lookup, New);
inserts an entry into the HashTable based on the value returned by the lookup and the call to the static function New.
2) As I understand from the second code statement in the ctor body, a key is inserted into the HashTable corresponding to the base class. How are the other types of patches entered into this table.
3) Finally what troubles me most is that I am unable to grasp when / where are these tables created during the creational process of fvMesh.
Thanks a lot for your attention.
Best Regards
Jaswi
|