Description
I would like to implement an error stack class/object. This will be helpful for addressing #76 (message for errors inside of stdlib). The reasons why this is useful are that:
- You can raise/signal errors deep within the call stack, even in
pure
(or maybeelemental
too, I'll have to think more carefully about that) procedures, and then handle them at a higher level - You can maintain a call stack with the object and optionally include the file name, procedure name, and line number along with an error message and error type/severity
- IO for warnings, errors, statuses, etc. can be performed once outside performance critical and/or pure/elemental procedures
- You could hook up some logging mechanism too, so that the file IO happens on return from
pure
procedures
Perhaps this should be part of #72 (standard assert and macros) or the existing stdlib_experimental_error.f90
or perhaps it should be its own module.
I'm waiting, in part, for #69 (string handling routines) to get to a point where there's some consensus, to take a crack at implementing that before proposing an implementation for this. String handling will be important for the manipulations required.
Sketch interface prototype
pure subroutine push(this, file_name, line, procedure_name)
!! caller calls this on line before call to callee
!! `file_name` can be set to a compile-time constant via CMake definition
!! or can use `__FILE__` macro, but this may overrun the line length
!! `line` can be passed `__LINE__` macro.
class(error_stack_t), intent(inout) :: this
character(len=*), intent(in) :: file_name
integer, intent(in) :: line
character(len=*), intent(in), optional :: procedure_name
end subroutine push
I like to let CMake ad a per-source-file definition, something like THIS_FILE
that holds the relative path to the file in the source directory. It could fallback to __FILE__
when using manual makefiles. The benefit over __FILE__
is two fold:
- Relative paths are shorter than absolute paths and less likely to overrun the line length limit, and CMake can check the length and insert a newline w/ continuation if needed
- Embedding absolute paths prevents builds from being reproducible and can make package managers thing that the software is not relocatable when it actually is.
Also, one or more arguments could be made optional. It would be wonderful if there was a way to query the name of the current scope in Fortran and if the programmer didn't need to make two separate calls, one to the callee and one push info onto the error stack. You could add file name, line number and error_stack arguments to all procedures and then the callee could push the data onto the stack as an alternative to the caller doing it.
Feedback, advice and suggestions welcome here.
pure subroutine pop(this)
!! Called before/on return to caller
!! only removes entries from the stack if no errors are signaling,
!! otherwise preserve the call stack to the error, but decrement the depth
!! so that when the error is caught the stack can be cleaned up appropriately
class(error_stack_t), intent(inout) :: this
end subroutine pop
pure subroutine signal(this, message, severity, error_type) ! Or raise?
!! Callee can signal or raise an exception and return control to caller
!! (or further up the call stack) for handling
class(error_stack_t), intent(inout) :: this
character(len=*), intent(in) :: message
integer, intent(in) :: severity
integer, intent(in), optional :: error_type
end subroutine signal
subroutine catch(this, pop) ! maybe impure elemental?
!! Do IO to tty or log as is necessary and handle calls to `error stop`
!! or other actions based on severity
class(error_stack_t), intent(inout) :: this
logical, optional :: pop !! pop stack up to this callers depth
end subroutine catch
The API needs some further thought, which is why I'd like feedback from others. In an ideal world, it would be awesome to have this act as a decorator and not have to call the push
method before the caller calls the callee or not have to pass the file name and line number if the callee does the push.
Preprocessor macro expansion could automatically expand multiple actual arguments on calls to other stdlib procedure to pass in the stack object, file name and line number but seems a bit too magic for my liking.