From 0bca6c3b2325ecd2bb78b89a9a8b121a3352fab6 Mon Sep 17 00:00:00 2001
From: Florian Prill <m300196@mlogin102.hpc.dkrz.de>
Date: Mon, 12 Sep 2016 09:59:33 +0200
Subject: [PATCH] implemented a simple fortran error handling framework.

---
 examples/example_hl.f90 |  56 +++++++++++++++++++++-
 src/Makefile.am         |   1 +
 src/Makefile.in         |   4 +-
 src/error_handling.f90  | 100 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 158 insertions(+), 3 deletions(-)
 create mode 100644 src/error_handling.f90

diff --git a/examples/example_hl.f90 b/examples/example_hl.f90
index 2fc18713..694b5f19 100644
--- a/examples/example_hl.f90
+++ b/examples/example_hl.f90
@@ -1,13 +1,44 @@
+MODULE mo_example
+
+  USE error_handling
+  IMPLICIT NONE
+
+CONTAINS
+
+  INTEGER FUNCTION division(number1, number2, ifail)
+    INTEGER,       INTENT(IN)    :: number1, number2
+    TYPE(t_error), INTENT(INOUT) :: ifail
+
+    division = 0
+    IF (number2 == 0) THEN
+      CALL create_error(ifail, t_arithmetic_error("Division by zero!"))
+    ELSE IF ((number1 < 0) .OR. (number2 < 0)) THEN
+      CALL create_error(ifail, t_invalid_input_error("Only positive numbers allowed!"))
+    ELSE
+      division = number1 / number2
+    END IF
+  END FUNCTION division
+END MODULE mo_example
+
+
+
 PROGRAM example
 
-  USE mtime,    ONLY: setCalendar, PROLEPTIC_GREGORIAN  
-  USE mtime_hl, ONLY: t_datetime, t_timedelta
+  USE mo_example
+
+  USE mtime,          ONLY: setCalendar, PROLEPTIC_GREGORIAN  
+  USE mtime_hl,       ONLY: t_datetime, t_timedelta
+  USE error_handling
   
   IMPLICIT NONE
   
   TYPE(t_datetime)  :: dt, dt2, dt3
   TYPE(t_timedelta) :: td
 
+  INTEGER       :: test_number1, test_number2, test_result
+  TYPE(t_error) :: ifail
+
+
   WRITE (0,*) "example_hl : test example"
 
   WRITE (0,*) "  setting calendar."
@@ -55,5 +86,26 @@ PROGRAM example
   WRITE (0,*) dt%to_string(), " == ", dt2%to_string(), ": ", (dt == dt2)
   dt3 = "1970-01-01T00:00:01"
   WRITE (0,*) dt%to_string(), " == ", dt3%to_string(), ": ", (dt == dt3)
+
+  ! ------------------------------------------------------------
+  ! EXAMPLE FOR ERROR HANDLING
+  ! ------------------------------------------------------------
+
+  WRITE (0,*) "EXAMPLE FOR ERROR HANDLING"
+  test_number1 = 10
+  test_number2 = -1
+  test_result  = division(test_number1, test_number2, ifail)
+
+  SELECT TYPE(info => ifail%info)
+  CLASS is (t_arithmetic_error)
+    WRITE (0,*) "I'm too stupid!"
+    CALL ifail%report()
+  CLASS is (t_invalid_input_error)
+    WRITE (0,*) "You are too stupid!"
+    CALL ifail%report()
+  CLASS default
+    WRITE (0,*) "Everything was ok!"
+    WRITE (0,*) "result = ", test_result
+  END SELECT
   
 END PROGRAM example
diff --git a/src/Makefile.am b/src/Makefile.am
index f4543686..327393f7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,6 +19,7 @@ libmtime_la_SOURCES = mtime_calendar.c \
 		      vsop87.c \
 		      kepler.c \
 		      orbit.c \
+                      error_handling.f90 \
                       libmtime.f90 \
                       libmtime_hl.f90
 
diff --git a/src/Makefile.in b/src/Makefile.in
index e0d85be5..0958997d 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -128,7 +128,8 @@ am_libmtime_la_OBJECTS = mtime_calendar.lo mtime_calendar360day.lo \
 	mtime_iso8601.lo mtime_date.lo mtime_datetime.lo \
 	mtime_julianDay.lo mtime_time.lo mtime_timedelta.lo \
 	mtime_eventList.lo mtime_eventHandling.lo mtime_utilities.lo \
-	vsop87.lo kepler.lo orbit.lo libmtime.lo libmtime_hl.lo
+	vsop87.lo kepler.lo orbit.lo error_handling.lo libmtime.lo \
+	libmtime_hl.lo
 libmtime_la_OBJECTS = $(am_libmtime_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -382,6 +383,7 @@ libmtime_la_SOURCES = mtime_calendar.c \
 		      vsop87.c \
 		      kepler.c \
 		      orbit.c \
+                      error_handling.f90 \
                       libmtime.f90 \
                       libmtime_hl.f90
 
diff --git a/src/error_handling.f90 b/src/error_handling.f90
new file mode 100644
index 00000000..ab3ea6f7
--- /dev/null
+++ b/src/error_handling.f90
@@ -0,0 +1,100 @@
+!> Simple framework for error handling in Fortran.
+!
+!  This error handling strategy has been described in:
+!
+!  Poppe, Cools,Vandewoestyne: 
+!  Error handling in Fortran 2003 (2012)
+!
+!  It benefits from a number of F2003 features.  In this
+!  implementation, some parts of the framework have been simplified.
+!
+!  Implementation: 09/2016, F. Prill (DWD)
+!
+MODULE error_handling
+
+  IMPLICIT NONE
+
+  PUBLIC :: t_error
+  PUBLIC :: t_error_info, t_invalid_input_error, t_arithmetic_error
+
+  PUBLIC :: create_error
+  PUBLIC :: transfer_error
+
+
+  INTEGER, PARAMETER :: MAX_ERROR_STR_LEN = 256
+
+
+  !> Base class for error info, storing the details of the
+  !  circumstances in which the error occured. Making a distinction
+  !  between the error and the associated information simplifies the
+  !  use.
+  TYPE t_error_info   
+    CHARACTER(LEN=MAX_ERROR_STR_LEN) :: error_string
+  END TYPE t_error_info
+
+
+  !> Error info type: Invalid user input
+  TYPE, EXTENDS(t_error_info) :: t_invalid_input_error
+  END TYPE t_invalid_input_error
+
+  !> Error info type: Arithmetic error
+  TYPE, EXTENDS(t_error_info) :: t_arithmetic_error
+  END TYPE t_arithmetic_error
+
+ 
+
+  !> Base class for errors
+  TYPE t_error
+    CLASS(t_error_info), POINTER :: info => NULL()
+  CONTAINS 
+    PROCEDURE :: report  => error_report
+    PROCEDURE :: discard => error_discard
+  END TYPE t_error
+
+CONTAINS
+
+  !> Called in an unexpected situation, this subroutine creates an
+  !  error, storing all parameters that are needed to uniquely define
+  !  the situation.
+  !
+  SUBROUTINE create_error(new_error, new_info)
+    TYPE(t_error),       INTENT(INOUT) :: new_error
+    CLASS(t_error_info), INTENT(IN)    :: new_info
+    ALLOCATE(new_error%info, source=new_info)
+  END SUBROUTINE create_error
+
+
+  !> Transfers the error from @p inform to the @p ifail argument.
+  !
+  !  Thus, the responsibility of the error can be transferred to the
+  !  calling procedure.
+  !
+  SUBROUTINE transfer_error(inform, new_error)
+    TYPE(t_error), INTENT(IN)    :: inform
+    TYPE(t_error), INTENT(INOUT) :: new_error
+    new_error%info => inform%info
+  END SUBROUTINE transfer_error
+
+
+  !> Outputs the given error.
+  !
+  SUBROUTINE error_report(this)
+    CLASS(t_error) :: this
+    IF (ASSOCIATED(this%info)) THEN
+      WRITE (0,*) "ERROR STRING: ", this%info%error_string
+    ELSE
+      WRITE (0,*) "SUCCESS"
+    END IF
+    CALL this%discard()
+  END SUBROUTINE error_report
+
+
+  !> Indicates that the given error has been taken care of and can be
+  !  silenced safely.
+  !
+  SUBROUTINE error_discard(this)
+    CLASS(t_error) :: this
+    IF (ASSOCIATED(this%info))  DEALLOCATE(this%info)
+  END SUBROUTINE error_discard
+
+END MODULE error_handling
-- 
GitLab