CFD Online Logo CFD Online URL
www.cfd-online.com
[Sponsors]
Home > Forums > Software User Forums > OpenFOAM > OpenFOAM Programming & Development

Runtime Type Selection

Register Blogs Community New Posts Updated Threads Search

Like Tree5Likes
  • 2 Post By Zeppo
  • 2 Post By Zeppo
  • 1 Post By Zeppo

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old   July 30, 2016, 17:10
Default Runtime Type Selection
  #1
Senior Member
 
Zeppo's Avatar
 
Sergei
Join Date: Dec 2009
Posts: 261
Rep Power: 21
Zeppo will become famous soon enough
I am looking into the essentials of runtime type selection mechanism (RTS) deployed in OpenFoam. Take polyPatch class as an example. The .h file:
Code:
//--- polyPatch.H ---
class polyPatch
{
public:
    //- Runtime type information
    TypeName("patch");
TypeName("patch") macro effectively expands into (omitting the things related to debug information):
Code:
static const char* typeName_() { return "patch"; }
static const ::Foam::word typeName;
virtual const word& type() const { return typeName; }
typeName is defined (initialised) in the .cpp file:
Code:
//--- polyPatch.C ---
namespace Foam
{
    defineTypeNameAndDebug(polyPatch, 0);
defineTypeNameAndDebug(polyPatch, 0) macro effectively expands into
Code:
const ::Foam::word polyPatch::typeName(polyPatch::typeName_());
It is the static member typeName that is used later on when the class's "virtual constructor" is inserted into RTS hash-table. Then what is typeName_ intended for? typeName could have been initialised like const ::Foam::word polyPatch::typeName("patch") with the macro defineTypeNameAndDebug being redefined properly to take a new parameter: defineTypeNameAndDebug(polyPatch, "patch", 0)
Linmunn and yueyun like this.
Zeppo is offline   Reply With Quote

Old   August 1, 2016, 18:44
Default
  #2
Senior Member
 
Zeppo's Avatar
 
Sergei
Join Date: Dec 2009
Posts: 261
Rep Power: 21
Zeppo will become famous soon enough
Below is the code responsible for runtime type selection (RTS) in class polyPatch. The code which is not relevant to RTS was left out for the sake of clearness.
Code:
//--- polyPatch.H ---
class polyPatch
{
public:
    //- Runtime type information
    TypeName("patch");

    // 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,
                const word& patchType
            ),
            (name, size, start, index, bm, patchType)
        );

    // Constructors
        //- Construct from components
        polyPatch
        (
            const word& name,
            const label size,
            const label start,
            const label index,
            const polyBoundaryMesh& bm,
            const word& patchType
        );

    // Selectors
        //- Return a pointer to a new patch created on freestore from
        //  components
        static autoPtr<polyPatch> New
        (
            const word& patchType,
            const word& name,
            const label size,
            const label start,
            const label index,
            const polyBoundaryMesh& bm
        );
What we have when the macro are expanded is the following:
Code:
//--- polyPatch.H ---
class polyPatch
{
public:
    //- Runtime type information
    static const char* typeName_() { return "patch"; }
    static const ::Foam::word typeName;

    // Declare run-time constructor selection tables

    //- Declare a run-time selection
    /* Construct from argList function pointer type */                  
    typedef autoPtr<polyPatch> (*wordConstructorPtr)
    (
        const word& name,
        const label size,
        const label start,
        const label index,
        const polyBoundaryMesh& bm,
        const word& patchType
    );       
                                                                        
    /* Construct from argList function table type */                    
    typedef HashTable<wordConstructorPtr, word, string::hash>     
        wordConstructorTable;                                     
                                                                        
    /* Construct from argList function pointer table pointer */         
    static wordConstructorTable* wordConstructorTablePtr_;  
                                                                        
    /* Table constructor called from the table add function */          
    static void constructwordConstructorTables();               
                                                                        
    /* Table destructor called from the table add function destructor */
    static void destroywordConstructorTables();                 
                                                                        
    /* Class to add constructor from argList to table */                
    template<class polyPatchType>                                      
    class addwordConstructorToTable                             
    {                                                                   
    public:                                                             
                                                                        
        static autoPtr<polyPatch> New
        (
            const word& name,
            const label size,
            const label start,
            const label index,
            const polyBoundaryMesh& bm,
            const word& patchType
        )
        {                                                               
            return autoPtr<polyPatch>(new polyPatchType 
                (name, size, start, index, bm, patchType));       
        }                                                               
                                                                        
        addwordConstructorToTable                               
        (                                                               
            const word& lookup = polyPatchType::typeName               
        )                                                               
        {                                                               
            constructwordConstructorTables();                   
            if (!wordConstructorTablePtr_->insert(lookup, New))   
            {                                                           
                std::cerr<< "Duplicate entry " << lookup                
                    << " in runtime selection table " << "polyPatch"      
                    << std::endl;                                       
                error::safePrintStack(std::cerr);                       
            }                                                           
        }                                                               
                                                                        
        ~addwordConstructorToTable()                            
        {                                                               
            destroywordConstructorTables();                     
        }                                                               
    };
Now look at file polyPatch.C
Code:
//--- polyPatch.C ---
namespace Foam
{
    defineTypeNameAndDebug(polyPatch, 0);

    defineRunTimeSelectionTable(polyPatch, word);

    addToRunTimeSelectionTable(polyPatch, polyPatch, word);
and its preprocessed counterpart:
Code:
//--- polyPatch.C ---
namespace Foam
{
    const ::Foam::word polyPatch::typeName(polyPatch::typeName_());

    /* Define the constructor function table */
    polyPatch::wordpolyPatchConstructorTable*
        polyPatch::wordConstructorTablePtr_ = NULL

    /* Table constructor called from the table add function */
    void polyPatch::constructwordConstructorTables()   
    {                                                         
        static bool constructed = false;                      
        if (!constructed)                                     
        {                                                     
            constructed = true;                               
            polyPatch::wordConstructorTablePtr_          
                = new polyPatch::wordConstructorTable;   
        }                                                     
    }

    /* Table destructor called from the table add function destructor */
    void polyPatch::destroywordConstructorTables()               
    {                                                                   
        if (polyPatch::wordConstructorTablePtr_)                   
        {                                                               
            delete polyPatch::wordConstructorTablePtr_;            
            polyPatch::wordConstructorTablePtr_ = NULL;            
        }                                                               
    }

    /* Add the thisType constructor function to the table */
    polyPatch::addwordConstructorToTable<polyPatch>   
        addpolyPatchwordConstructorTopolyPatchTable_
"Virtual constructor" New is defined in polyPatchNew.C:
Code:
//--- polyPatchNew.C ---
Foam::autoPtr<Foam::polyPatch> Foam::polyPatch::New
(
    const word& patchType,
    const word& name,
    const label size,
    const label start,
    const label index,
    const polyBoundaryMesh& bm
)
{
    wordConstructorTable::iterator cstrIter =
        wordConstructorTablePtr_->find(patchType);

    if (cstrIter == wordConstructorTablePtr_->end())
    {
        FatalErrorInFunction
            << "Unknown polyPatch type "
            << patchType << " for patch " << name << nl << nl
            << "Valid polyPatch types are :" << endl
            << wordConstructorTablePtr_->sortedToc()
            << exit(FatalError);
    }

    return autoPtr<polyPatch>
    (
        cstrIter()
        (
            name,
            size,
            start,
            index,
            bm,
            patchType
        )
    );
}
Why is class addwordConstructorToTable<polyPatchType> in the public access section? It's only purpose is to be instantiated (for a specific template parameter) once by polyPatch itself or by every class inherited from polyPatch which RTS is supposed to be applied to. Why not put it to the protected section? The same observation can be made regarding to member variable wordConstructorTablePtr_, it should be in the protected section too as the only places it is accessed from are class addwordConstructorToTable<polyPatchType> and static functions New and there seems to be no need in making it public. Protected should work just fine: addwordConstructorToTable<polyPatchType> is a nested class so it can access all the members (public, protected and private) of the nesting class; functions New are defined in the class polyPatch and in the derived classes so they can have access to any protected data in polyPatch.

Moreover, addpolyPatchwordConstructorTopolyPatchTable_ is a global object which means that it can be visible in every translation unit the library (shared object (.so)) it is built in is linked to. What's the point? All addpolyPatchwordConstructorTopolyPatchTable_ has to do is to be constructed and destroyed, literally it is addressed 2 times throughout the program runs. Declaring it as static polyPatch::addwordConstructorToTable<polyPatch> addpolyPatchwordConstructorTopolyPatchTable_ (or putting it to an unnamed namespace{})can narrow the scope it is visible from to the single translation unit it is defined in. Or even better choice, don't make it global, make it a static member defined in a private section of class polyPatch.

Typedefed types wordConstructorPtr and wordConstructorTable should be put in protected section as well (they are used in functions New of base class polyPatch and derived classes).

Now let me dissect function constructwordConstructorTables(), replicating its code below
Code:
/* Define the constructor function table */
    polyPatch::wordpolyPatchConstructorTable*
        polyPatch::wordConstructorTablePtr_ = NULL

    /* Table constructor called from the table add function */
    void polyPatch::constructwordConstructorTables()   
    {                                                         
        static bool constructed = false;
        if (!constructed)
        {                                                     
            constructed = true;                               
            polyPatch::wordConstructorTablePtr_          
                = new polyPatch::wordConstructorTable;   
        }                                                     
    }
wordConstructorTablePtr_ is a pointer (one of the POD (plain old data) types as they call it) and it has a "static duration" (all global and non-local static objects has it) which makes it being statically zero-initialised when the object code (library) is linked to other library or to the executable. This means that wordConstructorTablePtr_ is properly initialized (to a zero value) before constructwordConstructorTables() runs. This leads to the conclusion that static flag constructed isn't needed here and we can pretty safely address wordConstructorTablePtr_ from constructwordConstructorTables():
Code:
    void polyPatch::constructwordConstructorTables()   
    {                                                                            
        if (!polyPatch::wordConstructorTablePtr_)
        {                                                                                   
            polyPatch::wordConstructorTablePtr_          
                = new polyPatch::wordConstructorTable;   
        }                                                     
    }
Please note that I am nowhere near an expert (yet ) neither at C++ nor OpenFoam. Wise comments are welcome!
Linmunn and yueyun like this.

Last edited by Zeppo; August 2, 2016 at 14:12.
Zeppo is offline   Reply With Quote

Old   August 2, 2016, 16:58
Default
  #3
Senior Member
 
Zeppo's Avatar
 
Sergei
Join Date: Dec 2009
Posts: 261
Rep Power: 21
Zeppo will become famous soon enough
Here you can find an article on runtime type selection in OpenFoam: http://openfoamwiki.net/index.php/Sn...tion_mechanism
At the very bottom of the page it reads:
Quote:
Lastly, all Derived classes have one extra line in their Derived.C:
Code:
Base::addMrConstructorToTable<DerivedType>
    addDerivedTypeMrConstructorToBaseTable_;
Although this is in Derived.C, it is adding an instance of Base::AddToTable to the Base class. When Base is fully read, there will be a complete vtable with all the Derived constructors.
First, I don't think it is a good idea to call that HashTable object vtable. Yes, the concepts are very similar but they should not be mixed up inconsiderately.

Second, suppose you have Base and Derived in different libraries: Base is built into library libBase.so, and Derived is built into library libDerived.so. And you want to use these libraries in your application, linking them to you app at runtime. The only proper way to do so is when libBase.so is linked dynamically first so that the static hashtable (defined in class Base) is created and initialised. Then you can load (dynamically link) library Derived.so which creates a global object addDerivedTypeMrConstructorToBaseTable_ which in turn puts Derived's "virtual constructor" (a pointer to the static member function actually) into the hashtable.

When it comes to OpenFoam, class Base (being an interface class) is integral part of OpenFoam and is built into OpenFoam library (e.g finiteVolume.so or whatever). Implicitly, you make it dynamically linked through your file Make/options. The library libDerived.so can be instructed to be dynamically linked either implicitly in the same file Make/options or explicitly through the call to dlopen and dlsym in the code (controlDict's subdict libs should control it somehow). All this guarantees hashtable is initialised first and then securely filled with "virtual constructors" from derived classes.

p.s. Another decent article on the subject can be found here: http://www.sourceflux.de/blog/runtim...tion-openfoam/
yueyun likes this.
Zeppo is offline   Reply With Quote

Reply

Tags
rts, runtime type selection


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
Too high omega and k values in vortex flow simulation thomas. OpenFOAM Running, Solving & CFD 9 March 30, 2016 07:45
Boundary Conditions MtnRunBeachBum OpenFOAM Pre-Processing 1 April 30, 2015 17:33
LES supersonic free jet martyn88 OpenFOAM 22 April 17, 2015 07:00
interFoam/kOmegaSST tank filling with printStackError/Mules simpomann OpenFOAM Running, Solving & CFD 3 February 17, 2014 18:06
Problem with compile the setParabolicInlet ivanyao OpenFOAM Running, Solving & CFD 6 September 5, 2008 21:50


All times are GMT -4. The time now is 19:12.