Design » Object oriented design

Atlas is primarily written in the C++ programming language. The C++ programming language facilitates OO design, and is high performance computing capable.

The latter is due to the support C++ brings for hardware specific instructions. In addition, the high compatibility of C++ with C allows Atlas to make use of specific programming models such as CUDA to support GPU’s, and facilitates the creation of C-Fortran bindings to create generic Fortran interfaces.

Object oriented design in C++

Abstract interface (ObjectBase)

A commonly used feature in Atlas and in object-oriented programming is inheritance and polymorphism. This is used to define a common abstract interface method() in a class ObjectBase, with implementations in concrete classes ObjectA and ObjectB.

An example construction to create a concrete ObjectA in Modern C++ would be:

std::shared_ptr<ObjectBase> object{ new ObjectA( args... ) };

Now algorithms can be created accepting the abstract ObjectBase

void use_object( const ObjectBase& object ) {
    object.method();
}

...

use_object( *object );

Factory with self-registration (ObjectFactory, ObjectBuilder)

In above example the abstract object is hard-coded to be of concrete type ObjectA. You may want to have this configurable depending on a user-defined string object_type. You could then do:

std::shared_ptr<ObjectBase> object;
if( object_type == "A" ) {
    object = std::shared_ptr<ObjectBase>{ new ObjectA( args... ) };
}
if( object_type == "B" ) {
    object = std::shared_ptr<ObjectBase>{ new ObjectB( args... ) };
}

In order to avoid repeating this code in every place this is required, in Atlas we employ a Factory mechanism. with self-registration, so that the above code could be transformed to:

std::shared_ptr<ObjectBase> object = ObjectFactory::build( object_type, args... )

The method ObjectFactory::build() can in principle just wrap the above code, but for reasons of maintainability and more importantly extensibility, Atlas implements this using self-registration and an abstract ObjectBuilder as follows:

cpp_factory.png

All that is now needed to register a concrete ObjectBuilder is to place

static ObjectBuilderT<ObjectA> builder_A{ "A" };
static ObjectBuilderT<ObjectA> builder_B{ "B" };

anywhere in a global scope. A good place would be in the file where each concrete Object is defined. When the code is compiled into a shared library, then these builders are automatically registered in the ObjectFactory when the library is loaded at run-time.

Pointer to abstract implementation (Object)

Another idiom which is adopted in Atlas is the Pointer to implementation (PIMPL) idiom. This means that we create a class Object which contains as only data member a (shared) pointer to the implementation ObjectBase, but also mimics the public interface of ObjectBase but delegates execution to the encapsulated pointer:

cpp_pimpl.png

This certainly adds a maintainance cost to the Atlas core developers, as every public routine in ObjectBase must be reproduced in Object. It however adds several advantages for the user, and user-code:

  • Value semantics. You do not have to handle the raw pointer ObjectBase*, e.g. by creating a shared_ptr<ObjectBase>, and you do not need to use the -> operator. This also ensures that when the Object instance goes out of scope, the internal pointer gets deleted (if it is the only instance of the same shared pointer).
  • Factory builder. The creation of concrete types is embedded in the constructor of Object!
  • A compilation firewall. This is achieved because it is not required to #include <ObjectBase.h> inside Object.h (only a forward declaration suffices as it is a pointer).

Object oriented design in Fortran

With much of the NWP operational software written in Fortran, significant effort in the Atlas design has been devoted to having a Fortran OO Application Programming Interface (API) wrapping the C++ concepts as closely as possible.

The Fortran API mirrors the C++ classes with a Fortran derived type, whose only data member is a raw pointer to an instance of the matching C++ class. The Fortran derived type also contains member functions or subroutines that delegate its implementation to matching member functions of the C++ class instance. Since Fortran does not directly interoperate with C++, C interfaces to the C++ class member functions are created first, and it is these interfaces that the Fortran derived type delegates to. The whole interaction procedure is schematically shown:

Image alt text
This website is beyond its original expiry date and the content may be out of date. The site owner has been notified and may choose to extend the expiry date and remove this banner. If you have any questions about this, please visit our support portal.