b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2object.H File Reference
#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"
Include dependency graph for b2object.H:
This graph shows which files directly or indirectly include this file:

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.
 

Detailed Description

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++.

Motivation for the B2000++ Object concept

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):

if (name == "R2") {
element_r2_set_nodes(...);
} else if (name == "Q4") {
element_q4_set_nodes(...);
...

While straightforward, this technique requires a lot of code duplication for each set of subroutines, for example:

if (name == "R2") {
element_r2_get_value(...);
} else if (name == "Q4") {
element_q4_get_value(...);
...

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:

Element* e = 0;
if (name == "R2") {
e = new ElementR2();
} else if (name == "Q4") {
e = new ElementQ4();
...

Thereafter, the polymorphic (virtual) functions can be used regardless of the type:

e->set_nodes(...);
e->get_value(...);

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.

b2000::Object and b2000::ObjectType

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.

How to register an implementation of an Object 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):

class ElementQ4Stress2DExample : public b2000::TypedElement<double> {
public:
...
using type_t = b2000::ObjectTypeComplete<
ElementStress2DExample,
static type_t type;
};
Definition b2object.H:415
Definition b2object.H:340
Definition b2element.H:1054
static type_t type
Element interface typed by the dof.
Definition b2element.H:1673

The static member type is initialized as follows:

ElementQ4Stress2DExample::type_t
ElementQ4Stress2DExample::type(
"Q4.S.2D.EXAMPLE", // Name of ElementQ4Stress2DExample.
"", // Suffix for name and aliases.
StringList(), // List of aliases (empty in this example).
element::module, // The parent module.
&b2000::TypedElement<double>::type // ObjectType of super class.

How to create an instance of an Object sub-class

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

  • b2000.element: The name of the shared library is 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:

try {
// Get a pointer to the factory object.
b2000::Element::type_t* eltype = b2000::Element::type.get_subtype(
"Q4.S.2D.EXAMPLE", b2000::element::module);
// Create an instance of ElementQ4Stress2DExample.
b2000::Element* e = eltype->new_object();
assert(dynamic_cast<ElementQ4Stress2DExample*>(e) != 0);
} catch (b2000::KeyError&) {
...
}
Defines the complete interface for Element instances (C++ representations of Finite Elements).
Definition b2element.H:156
Definition b2exception.H:276
ObjectTypeIncomplete * get_subtype(const std::string &name_, Module &module_=b2000_module)
Definition b2object.H:369
OBJECT * new_object()
Definition b2object.H:348