Filter löschen
Filter löschen

Create MException object with a custom non-empty stack (without actually throwing it)

6 Ansichten (letzte 30 Tage)
Dear Matlab Community,
Is it possible to create a valid MException object with a non-empty stack without actually throwing it?
The goal is to execute something somewhere in the code hierarchy and collect the occuring errors with a purpose to process them later in the top-level GUI code, e.g.
function errs = myMethod( this, args )
errs = MException.empty();
% ... do some safe stuff ...
try % ... do some unsafe stuff ...
catch err, errs = [ errs; err ];
% ... do some cleanup ...
return;
end
% ... do more safe stuff ...
try % ... do more unsafe stuff ...
catch err, errs = [ errs; err ];
% ... do some cleanup ...
return;
end
% continue
end
In the example above, the ERR output value will be either an empty array of MExceptions (i.e. no errors), or a scalar MException including the complete stack of the error location, or a vector of such. The actual code may have more hierarchy levels, i.e.
function errs = myMethod( this, args )
someErrs = this.doSomeStuff( someArgs );
moreErrs = this.doMoreStuff( moreArgs );
restErrs = this.doRestStuff( restArgs );
errs = [ someErrs; moreErrs; restErrs ];
end
... where each called submethod or function has similar ERRS output, which are collected to a single vector in the top-level code.
My question is, whether this is possible to locally create a custom MException with a complete local or custom stack without actually throwing it, e.g.:
% ... do some safe stuff ...
if ( someErrorCondition )
% do specific cleanup
% create an exception, but don't throw it, just collect
err = MException( 'myFunc:myErr', 'my error message', dbstack() );
errs = [ errs; err ];
end
% ... do more safe stuff ...
Unfortunately, the syntax above will not work (third "stack" argument is not supported), the stack field of MException is only set by throwing it, and is read-only for an existing MException.
The only way to create an MException object with a custom non-empty stack is seemingly to actually manually throw an exception using error( errStruct ) syntax:
% ... do some safe stuff ...
if ( someErrorCondition )
% do specific cleanup
% create an error specification structure with a custom stack
err = struct( 'identifier', 'myFunc:myErrr', 'message', 'my error message', 'stack', dbstack() );
% throw an exception and catch it immediately,
% then it will have a non-empty user-defined stack
try error( err ); catch err; end
% collect an exception
errs = [ errs; err ];
end
% ... do more safe stuff ...
The latter way works, but seems to be rather ugly and possibly includes a significant overhead to throw-and-catch an error, especially if this happens multiple times for multiple errors.
A possible solution would be to collect the errors as a cell array instead of an array of MExceptions. This would allow using some alternative error format, but would also require a dedicated error-format-dependent error processing code at the top-level, and this is what I would prefer to avoid as long as there's a way to use the native MExceptions.
So,
– is there a way to create an MException object with a custom stack, or
– is there some typical alternative approach for such multiple error handling?
Thanks in advance!

Antworten (1)

Steven Lord
Steven Lord am 21 Apr. 2022
Preallocate a cell array.
M = cell(1, 3);
Run the code that may throw an error. If it does, catch the error in the appropriate element of the cell array. I'll do this three times, where the first and third do error and the second does not.
try
x = [1 2] + [1 2 3]; % Error, can't add a 1-by-2 and a 1-by-3
catch ME
M{1} = ME;
end
try
q = 1:10; % Does not error
catch ME
M{2} = ME;
end
try
y = [1 2; 3 4]*magic(3); % Error, can't multiply a 2-by-2 and a 3-by-3
catch ME
M{3} = ME;
end
Let's look at the cell array. The first and third cells are non-empty, the second is empty.
celldisp(M)
M{1} = MException with properties: identifier: 'MATLAB:sizeDimensionsMustMatch' message: 'Arrays have incompatible sizes for this operation.' cause: {} stack: [3×1 struct] Correction: [] M{2} = [] M{3} = MException with properties: identifier: 'MATLAB:innerdim' message: 'Incorrect dimensions for matrix multiplication. Check that the number of columns in the first matrix matches the number of rows in the second matrix. To operate on each element of the matrix individually, use TIMES (.*) for elementwise multiplication.' cause: {} stack: [3×1 struct] Correction: []
When we concatenate the three cells together using a comma-separated list the empty cell basically disappears, leaving us our two errors.
theErrors = [M{:}]
theErrors =
1×2 MException array with properties: identifier message cause stack Correction
The first is M{1} and the second is M{3}.
isequal(theErrors(1), M{1})
ans = logical
1
isequal(theErrors(2), M{3})
ans = logical
1
Is theErrors the MException array that you were hoping to create?
  1 Kommentar
Sergey Miropolsky
Sergey Miropolsky am 21 Apr. 2022
Steven, thanks for the proposal! Collecting the exceptions using a cell array is certainly one of the options to consider. However it does not solve the main issue, which is "how to manually create an MException object with a non-empty user-defined stack field without actually throwing this exception".
Typically, when an exception is caught, the object has three fields: identifier, message and stack.
err =
MException with properties:
identifier: 'err:err'
message: 'message'
cause: {0x0 cell}
stack: [3x1 struct]
The stack here is the current function call stack, similar to one obtained with dbstack() function.
err.stack(1) =
file: 'myPath\myFile.m'
name: 'myFunction1'
line: 123
err.stack(2) =
file: 'myPath\myFile.m'
name: 'myFunction2'
line: 456
This stack from MException can then be displayed as a part of the error message at the top GUI level for further debug.
In opposite, when an exception object is created manually on some condition, e.g.
if ( someErrorCondition )
ME = MException( 'err:err', 'message' );
end
the stack field is always empty and write-protected.
ME =
MException with properties:
identifier: 'err:err'
message: 'message'
cause: {0x0 cell}
stack: [0x0 struct]
The only way to set the stack field is to actually throw and catch the exception, then the stack field is set somewhere inside throw().
try throw( ME ), catch ME; end
% --> now ME stack is implicitly set to <here>
This also works with error(), but in any case the try/catch block seems to be mandatory.
So the question is, can the stack of MException be set in any other simpler way, except for throwing it? Is this an intended behaviour? Why?

Melden Sie sich an, um zu kommentieren.

Kategorien

Mehr zu Error Handling finden Sie in Help Center und File Exchange

Produkte

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by