#include <map>#include <sstream>#include <string>#include "b2ppconfig.h"#include "utils/b2allocator.H"#include "utils/b2exception.H"#include "utils/b2module.H"#include "utils/b2util.H"

Go to the source code of this file.
Classes | |
| class | b2000::ObjectType |
| class | b2000::ObjectTypeIncomplete< OBJECT, BASE_OBJECT_TYPE > |
| class | b2000::ObjectTypeComplete< OBJECT, BASE_OBJECT_TYPE > |
| class | b2000::Object |
Namespaces | |
| namespace | b2000 |
| Contains the base classes for implementing Finite Elements. | |
Implementations of elements, material models, boundary conditions, solvers, etc. are all sub-classes of b2000::Object. Instances of b2000::ObjectType are factory objects which create instances of specific sub-classes ofb2000::Object and which, in conjunction with b2000::Module, allow for the implementation of plugins without having to recompile B2000++.
The motivation for b2000::Object and b2000::ObjectType is illustrated by means of an example. In traditional Finite Element codes that are written with procedural languages such as Fortran or C, element subroutines like set_nodes() or get_value(), i.e computation of first and second variation, are called within if/else constructs. Example (pseudo-code):
While straightforward, this technique requires a lot of code duplication for each set of subroutines, for example:
This duplication makes the code hard to maintain. A better approach consists of the object-oriented concept, where functions and data are grouped in polymorphic classes that derive from a common base class. It is the necessary to define if/else constructs only once, for the instantiation of the objects:
Thereafter, the polymorphic (virtual) functions can be used regardless of the type:
While this approach is more elegant, it does not allow for dynamically adding new elements without having to recompile B2000++.
However, b2000::Object in conjunction with b2000::ObjectType allows to register sub-classes of b2000::Object under a names (and several aliases), such that no if/else constructs are necessary at all.
Classes whose instances should be created dynamically should be sub-classes of b2000::Object and must override the static member named type. This static member is derived from b2000::ObjectType and serves for registration and as a factory object for instances of that sub-class.
Using this static type member, one can, by means of b2000::ObjectType::get_subtype(), get the factory object of a sub-class, and, by means of b2000::ObjectType::new_object(), create instances of this sub-class.
The implementation of an Object sub-class is explained by means of an example. Note that it must be done for every sub-class of b2000::Object (thus, also for sub-classes of b2000::Element, b2000::ElementProperty, b2000::Solver, etc.).
The sub-classElementQ4Stress2DExample implements a quadrilateral 2D stress element and is derived from b2000::TypedElement<double>, which itself is derived from b2000::Element. To register (under the name Q4.S.2D.EXAMPLE):
The static member type is initialized as follows:
To this end, the b2000::ObjectType::get_subtype() method is used. This is demonstrated by means of the following example. The ElementQ4Stress2DExample class may be provided by one of the modules
libb2000++.so (for builtin element implementations)-b2000.element.Q4.S.2D.EXAMPLE: The name is libb2000.element.Q4.S.EXAMPLE.so (for external element implementations)The ObjectType::get_subtype() function works for both cases, since it will attempt to load the shared library libb2000.element.Q4.S.EXAMPLE.so if Q4.S.EXAMPLE is not found in theb2000.element module.
The following code is a simplification of the Domain implementation: