Design Considerations

O2scl

Design contents

Design introduction

The design goal is to create an object-oriented computing library with classes that perform common numerical tasks. The most important principle is that the library should add functionality to the user while at the same time retaining as much freedom for the user as possible and allowing for ease of use and extensibility. A few recommendations:

  • The classes which utilize user-specified functions should be able to operate on member functions without requiring a particular inheritance structure,

  • The interfaces should be generic where possible so that the user can create new classes which perform related numerical tasks through inheritance.

  • The classes should avoid static variables or static functions.

  • Const-correctness and type-safety should be respected wherever possible.

Header file dependencies

For reference, it is useful to know how the top-level header files depend on each other, since it can be difficult to trace everything down. The following are the most “top-level” header files and their associated dependencies within O₂scl (there are other dependencies on GSL and the C standard library not listed here). Note that not all of the headers in the “base” directory are listed here (because they are less likely to cause problems)

Tier 1
  • constants.h : (none)

  • err_hnd.h : (none)

Tier 2
  • exception.h : err_hnd.h

  • misc.h : err_hnd.h

  • find_constants.h : constants.h

  • rng.h (in the mcarlo directory) : err_hnd.h

  • tridiag.h (in the linalg directory) : err_hnd.h

Tier 3
  • string_conv.h : misc.h

Tier 4
  • calc_utf8.h : rng.h err_hnd.h string_conv.h find_constants.h

  • columnify.h : misc.h string_conv.h

  • uniform_grid.h : string_conv.h err_hnd.h

  • format_float.h : err_hnd.h misc.h string_conv.h

Tier 5
  • funct.h : err_hnd.h calc_utf8.h

  • vector.h : misc.h uniform_grid.h (vector_special.h)

  • mm_funct.h : calc_utf8.h

  • multi_funct.h : calc_utf8.h

Tier 6
  • convert_units.h : find_constants.h calc_utf8.h misc.h string_conv.h vector.h constants.h

  • search_vec.h : err_hnd.h vector.h misc.h

  • permutation.h : vector.h

  • interp.h : search_vec.h tridiag.h vector.h

Tier 7
  • lib_settings.h : convert_units.h find_constants.h

The interpolation, testing, and table headers are not as top-level as the ones above because they depend on tridiagonalization in the linear algebra directory:

interp.h : search_vec.h tridiag.h vector.h
table.h : misc.h interp.h shunting_yard.h
table_units.h : table.h lib_settings.h
test_mgr.h : string_conv.h misc.h table_units.h

The use of templates

Templates are used extensively, and this makes for longer compilation times so any code that can be removed conveniently from the header files should be put into source code files instead.

Error handling

Thread safety for errors

Two approaches to thread-safe error handling which are worth comparing: the first is GSL which uses return codes and global function for an error handler, and the second is the Math/Special Functions section of Boost, which uses a separate policy type for each function. One issue is thread safety: the GSL approach is thread safe only in the sense that one can in principle use the return codes in different threads to track errors. What one cannot do in GSL is use different user-defined error handlers for different threads. The Special Functions library allows one to choose a different Policy for every special function call, and thus allows quite a bit more flexibility in designing multi-threaded error handling.

Memory allocation functions

Several classes have allocate() and free() functions to allocate and deallocate memory. If an error occurs in an allocate() function, the function should free() the partial memory that was allocated and then call the error handler. Functions which deallocate memory should never fail and should never be required to call the error handler. Similarly, class destructors should never be required to call the error handler.

Define constants and macros

There are a couple define constants and macros that O₂scl understands, they are all in upper case and begin with the prefix O2SCL_.

Range-checking for arrays and matrices is turned on by default, but can be turned off by defining O2SCL_NO_RANGE_CHECK during the initial configuration of the library. To see how the library was configured at runtime, use the o2scl::o2scl_settings class.

There is a define constant O2SCL_NO_SYSTEM_FUNC which permanently disables the shell command '!' in cli (when the constant is defined, the shell command doesn’t work even if o2scl::cli::shell_cmd_allowed is true).

The constant O2SCL_DATA_DIR is defined internally to provide the directory which contains the O₂scl data files. After installation, this can be accessed in o2scl::o2scl_settings.

All of the header files have their own define constant of the form O2SCL_HEADER_FILE_NAME which ensures that the header file is only included once.

Finally, I sometimes comment out sections of code with:

#ifdef O2SCL_NEVER_DEFINED
...
#endif

This constant should not be defined by the user as it will cause compilation to fail.

Parameter ordering

In functions where this makes sense, generally input parameters will appear first, while output parameters or parameters which handle both input and output will appear later.

Global objects

There four global objects that are created in libo2scl:

All other global objects should be avoided.

Thread safety

Most of the classes are thread-safe, meaning that two instances of the same class will not clash if their methods are called concurrently since static variables are only used for compile-time constants. Additionally, two threads should be able to safely call a const method for the same instance. However, two threads cannot, in general, safely modify the same instance of a class. In this respect, O₂scl is no different from GSL.