The Result type is an addon to the framework and is designed as an object to be returned from any procedures that may throw an error. It consists of data (i.e., what the function should return if there aren't any errors) and an array of ErrorInstances.
Fortran lacks any real kind of array-rank polymorphism (assumed rank arrays currently have very limited implementation and scope, and the select rank
construct has even less support) and creating a Result object able to store both type- and rank-polymorphic data proved impossible. As such, a different type exists for each rank, up to rank-4 (4 dimensional) data: type(Result0D)
, type(Result1D)
, type(Result2D)
, type(Result3D)
and type(Result4D)
. These all extend from the parent type(Result)
, which can be instantied itself to provide a Result object with no data (e.g., for returning an error from functions that would normally be subroutines).
Extending to high ranks is trivial - take a look at the Result.f90 file to see how it's done. If you do this, it is strongly encouraged to place the extension in a separate file so that you can later update the framework without conflicting with or overriding the changes you've made.
A generic Result(data, error, errors)
interface can be used to construct the Result object with a type corresponding to the rank of the data that you provide. If no error is provided, the errors array is set to contain only the default "no error" with code 0.
Parameter declaration | Description | Default |
---|---|---|
class(*) :: data , either scalar or ranks 1 to 4 |
The data to store. | - |
type(ErrorInstance), optional :: error |
An error, which is stored as the first and only item in the Result object's errors array | - |
type(ErrorInstance), optional :: errors(:) |
Errors to store. | - |
For example:
type(ErrorInstance) :: randomError
type(ErrorInstance) :: anotherError
type(Result0D) :: r0D
type(Result1D) :: r1D
randomError = ErrorInstance(42, "A random error.")
r0D = Result(data=1, errors=[randomError,anotherError])
r1D = Result(data=[1,2], error=randomError)
The data property is an unlimited polymorphic object, class(*)
, and so any object (or array of the same type objects) can be stored in it. However, this does means that a select type
construct or transfer()
function must be used when performing operations with the data that are returned from the Result object (using Result%getData()
), to avoid type conversion errors.
For your convenience, data for the most common types and kinds have pre-defined functions and corresponding operators that use the select type
construct to return the data as the specified type. These follow the same kind conventions as described in the ErrorCriteria docs for integers and reals.
The following functions are generics that will work with any rank of Result object that has data (Result0D
, Result1D
, etc.), and for non-scalar data, they return an array of the specified type. If they are unsuccessful returning the data as the specified type, an error will be thrown.
Return type and kind | Function | Operator | Implicit conversion |
---|---|---|---|
integer |
Result%getDataAsInteger() |
.integer. |
If data is real , real(dp) or real(qp) , the nearest integer will be returned |
real |
Result%getDataAsReal() |
.real. |
If data is integer , real(dp) or real(qp) , it will be converted to real |
real(dp) |
Result%getDataAsRealDP() |
.dp. |
If data is integer , real or real(qp) , it will be converted to real(dp) |
real(qp) |
Result%getDataAsRealQP() |
.qp. |
If data is integer , real or real(dp) , it will be converted to real(qp) |
character(:) |
Result%getDataAsCharacter() |
.character. |
|
logical |
Result%getDataAsLogical() |
.logical. |
|
complex |
Result%getDataAsComplex() |
.complex. |
For example:
type(Result0D) :: r0D
type(Result1D) :: r1D
r0D = Result(data=1.23_dp)
r1D = Result(data=['Hello','World'])
print *, .integer. r0D, & ! real(dp) data will be converted
.real. r0D, &
.dp. r0D, &
.qp. r0D
print *, .character. r1D
print *, r1D%getDataAsCharacter() ! This is equivalent to the previous line
print *, .integer. r1D ! This will throw an error
This results in:
$ 1 1.23000002 1.2300000000000000 1.22999999999999998223643160599749535
$ HelloWorld
$ HelloWorld
$ ERROR STOP Error trying to return 1D data as INTEGER. Are you sure the data is of type INTEGER?
If you wish to fill the data
property outside of the Result()
interface, then a Result%setData(data)
method exists.
Result objects store an array of ErrorInstances, making it possible to return multiple errors from functions. Result%getErrors()
returns this array of errors, whilst Result%getError()
returns the first error in this array. Two operators map directly to these functions: .errors.
and .error.
, respectively:
type(Result) :: r
r = Result(error=ErrorInstance(100, "A warning.", .false.))
call EH%trigger(error = .error. r) ! Equivalent to `call EH%trigger(error=r%getError())`
r = Result(errors=[ &
ErrorInstance(100, "A warning.", .false.), &
ErrorInstance(200, "An error.", .true.) &
])
call EH%trigger(errors = .errors. r) ! Equivalent to `call EH%trigger(errors=r%getErrors())`
This results in:
$ Warning: A warning.
$ Warning: A warning.
$ Error: An error.
$ ERROR STOP 200
Result%getErrorCode()
returns the integer code of the first error in the errors array.
Once a Result object has been created, you can add further errors to the array of errors stored by it, by using the Result%addError(error)
and Result%addErrors(errors)
functions.
Add a single error to the Result object.
Parameter declaration | Description | Default |
---|---|---|
type(ErrorInstance) :: error |
The error to add to the Result object's array of errors. | - |
Add an array of errors to the Result object.
Parameter declaration | Description | Default |
---|---|---|
type(ErrorInstance) :: errors(:) |
The errors to add to the Result object's array of errors. | - |
You can use the Result%hasError()
and Result%hasCriticalError()
procedures to check whether a Result object has an error (and that error isn't the default no error, with code 0), and whether it has a critical error, respetively. Both return a logical value.
At some point, it might be desirable to remove all errors from the Result objects. For example, if those errors have already been triggered by the ErrorHandler
and shouldn't be triggered a second time. The Result%clear()
simply deallocated the Result%errors
array.
The procedure Result%addToTrace(message)
can be used to add the same node to each of the Result object's errors. See the ErrorInstance docs.