我想为 Matlab 代码使用面向对象的设计,然后将其转换为 C,最后它应该用于 DSP - 处理器。
我不确定的是如何进行错误处理。据我所知,在 C 中它应该与枚举一起使用,如果我在我的 matlab 代码中使用异常,我不确定 matlab 将如何将它转换为 C。
另一种可能性是实现枚举类并使用它而不是异常。
由于我在软件架构方面没有太多经验,因此欢迎任何建议。
我想为 Matlab 代码使用面向对象的设计,然后将其转换为 C,最后它应该用于 DSP - 处理器。
我不确定的是如何进行错误处理。据我所知,在 C 中它应该与枚举一起使用,如果我在我的 matlab 代码中使用异常,我不确定 matlab 将如何将它转换为 C。
另一种可能性是实现枚举类并使用它而不是异常。
由于我在软件架构方面没有太多经验,因此欢迎任何建议。
这是一个相当古老的问题,但我认为这是一个非常重要的问题,因为它涉及到一些关于如何最好地使用 MATLAB Coder 和构建代码的基本问题。
一个好的错误处理系统对于任何中等规模的代码库都非常重要,因此使其结构化很重要。在针对 MATLAB Coder 的代码中,您不能使用任何正常的异常函数(try、catch、error 等),甚至“断言”在 Coder 中也具有特殊含义。这意味着您需要自己创建一个良好的错误处理系统。这是不幸的,但并非不可克服。
您可以使用不同的错误代码创建一个枚举类型并返回适当的枚举。这是一种非常标准的 C 做事方式,它可以非常清晰地从 MATLAB 转换为 C。它有几个缺点:
这些都是内置 MATLAB 系统确实提供的所有东西,所以拥有其中一些东西会很好。
在我的代码库中,我开发了一个非常适合我的错误系统。它使用 Coder 中一些强大的工具来制作一个干净、易于使用的错误系统,看起来有点像 MATLAB 自己的错误系统。它还为最终调用 Coder 生成的库的程序中的错误处理提供了很好的连接。
第一个函数是错误容器。这是一个包含存储运行时错误的持久变量的函数。
function [ idnum, errorid, errorstring, inneridnum ] = errorcontainer(functionname, varargin ) %#codegen
%ERRORCONTAINER The container function for error processing
% This function contains the error ID table, and can assign new error
% codes or look up error codes.
%
% Examples:
% To register a new error and retrieve a unique error ID:
% idnum = errorcontainter( 'register', ID, MESSAGE )
% where ID and MESSAGE are strings. ID is a short machine readable
% string, and MESSAGE is human-readable.
%
% Optionally, an INNERIDNUM can be provided to indicate an error code for
% a inner (lower-level) error related to this. This will allow
% chaining of error messages:
%
% idnum = errorcontainter( 'register', ID, MESSAGE, inneridnum )
%
% ID and MESSAGE can have maximum lengths of 2048 characters. Anything
% longer will be truncated.
%
% e.g. idnum = errorcontainer('register', ...
% 'FOO_INVALIDTYPE', ...
% 'First input must be an int.');
% idnum will be a negative int32 returned.
%
% If the ID matches an existing ID, the message will be overwritten
% and the same idnum will be returned (the database does not grow).
%
% To lookup an existing error code:
% [idnum, errorid, errorstring] = errorcontainer('lookup', idnum);
%
% See also: errorcode2string, registererror
persistent IDLIST;
persistent ERRORSTRINGLIST;
persistent INNERID;
width = 2048;
if isempty(IDLIST)
IDLIST = char(zeros(1,width));
tempstr = 'ERRORCONTAINER_UNKNOWNERRORID';
IDLIST(1,1:numel(tempstr)) = tempstr;
end
if isempty(ERRORSTRINGLIST)
ERRORSTRINGLIST = char(zeros(1,width));
tempstr = 'Unknown Error';
ERRORSTRINGLIST(1,1:numel(tempstr)) = tempstr;
end
if isempty(INNERID)
INNERID = zeros(1,1,'int32');
end
coder.varsize('IDLIST', 'ERRORSTRINGLIST', 'INNERID', [], [1,1]);
coder.varsize('errorstring', 'errorid');
switch lower(functionname)
case 'register'
% First see if the listed ID matches any in the database.
errorid = varargin{1};
if numel(errorid) > width
errorid = errorid(1:width);
end
if (nargin == 4)
inneridnum = int32(varargin{3});
else
inneridnum = int32(0);
end
errorstring = varargin{2};
if numel(errorstring) > width
errorstring = errorstring(1:width);
end
matchindex = 0;
for i = 1:size(IDLIST,1)
if ( strcmpi(errorid, deblank(IDLIST(i,:))) && (inneridnum == INNERID(i) ) )
matchindex = i;
end
end
if (matchindex > 0)
idnum = int32(-matchindex);
else
idnum = int32(-(size(IDLIST,1)+1));
tempstr = char(zeros(1,width));
tempstr(1:numel(errorid)) = errorid;
IDLIST = [IDLIST ; tempstr]; % In Coder, cannot grow with indexing. Have to concatinte and reassign.
tempstr = char(zeros(1,width));
tempstr(1:numel(errorstring)) = errorstring;
ERRORSTRINGLIST = [ERRORSTRINGLIST ; tempstr];
INNERID = [INNERID; inneridnum];
end
case 'lookup'
idnum = varargin{1};
tidnum = idnum;
if ((-tidnum > size(IDLIST,1)) || (-tidnum <= 0 ))
tidnum = int32(-1);
end
errorid = deblank(IDLIST(-tidnum,:));
errorstring = deblank(ERRORSTRINGLIST(-tidnum,:));
inneridnum = INNERID(-tidnum);
otherwise
idnum = int32(-1);
errorid = deblank(IDLIST(-idnum,:));
errorstring = deblank(ERRORSTRINGLIST(-idnum,:));
inneridnum = int32(0);
end
end
这个函数的核心是一个大的 switch 语句,它允许选择要执行的任务。errorcontainer 函数旨在作为错误处理系统的内部存储和服务例程。
用于“抛出”错误的主要函数是registererror
函数,如下所示:
function idnum = registererror(messageid, errorstring, varargin) %#codegen
%REGISTERERROR Registers a message id and error string with error system
% Given a message ID string and an error code string (human-readable),
% will register the strings with the error subsystem and generate a
% negative ID number to return.
%
% Example:
% idnum = registererror('FOO_INVALIDINPUT', 'Invalid input to function foo');
%
% Optionally, an inner id number can be handed to the registration to
% assocaite this error with a lower-level error previously registered.
% idnuminner = registererror('BAR_INTERNALERROR', 'Internal error in bar');
% idnum = registererror('FOO_INVALIDINPUT', 'Invalid input to function foo', idnuminner);
%
% See Also: errorcode2string, errorcontainer
coder.inline('never')
switch nargin
case 2
idnum = errorcontainer('register', messageid, errorstring);
case 3
idnum = errorcontainer('register', messageid, errorstring, varargin{1});
end
end
该registererror
函数接受一个错误标记字符串(通常是发生错误的函数名称,后跟一个冒号,然后是发生了什么的一些标识符)和一个人类可读的错误消息。它在全局存储中注册错误并以 int32 形式返回错误标识符。我的代码的规则是 int32(0) 的值表示没有错误,负值是这些注册的错误标识符之一。
registererror 返回的值不是每个错误条件唯一的。它们是错误容器的引用句柄,可用于检索错误标记字符串和人类可读的字符串。
在我的代码中, registererror 的使用通常是这样的:
...
output = someFunctionThatShouldReturnZeroIfSuccessful(filename);
if (output ~= 0)
result = registerror('MYFUNC:THESOMEFUNCTIONFAILED', ['The function that should return zero returned something else for filename: ', filename]);
return;
end
...
在人类可读的字符串中,我可以附加来自实际执行的字符串数据。在这个例子中,我附加了在我的理论函数中失败的文件名。我将所有函数的返回值设为 int32,所有这些都可以来自对 registererror 的调用。
还有一个额外的可选调用 registererror。方便做的一件事是创建一堆错误。有时更深层次的函数中的错误会导致问题,我们想要完整的堆栈跟踪。这很容易做到,因为可以选择将内部错误 ID 交给 registererror。让我们看一个例子:
...
result = someFunctionThatShouldReturnZeroIfSuccessful(filename);
if (result ~= 0)
result = registerror('MYFUNC:THESOMEFUNCTIONFAILED', ['The function that should return zero returned something else for filename: ', filename], result);
return;
end
...
在这种情况下, someFunction... 返回了一个值,该值本身是通过调用 registererror 生成的,并且该结果值作为第三个参数添加到此处的 registererror 调用中,这将返回一个不同的错误值。我们可以用这个值做什么?
好吧,我们需要第三个函数,它通常可以使用我们的库通过代码调用。该函数称为errorcode2string
,顾名思义,它可以采用我们的错误代码之一并返回两个字符串,以及与错误相关的任何内部错误代码。这是errorcode2string:
function [errorid, errorstring, innercode] = errorcode2string(errorcode) %#codegen
%ERRORCODE2STRING Return strings given an error code
% Given an error code returned by any of the library functions, will
% return a string with human-readable information about the error. The
% error codes are, in some cases, dynamically generated, so the codes
% themselves should not be used for programmatic flow control except that
% a negative value in a return always indicates an error occurred.
%
% Example:
% [errorid, errorstring, innercode] = errorcode2string(errorcode);
%
% - errorcode is an int32 value.
%
% - errorid is a string that is machine-readable and can be used to trap
% specific error codes.
%
% - errorstring is the returned 1 by N string of the human-readable error
% information.
%
% - innercode is the int32 error code of an inner error message if any.
% It is a negative value of an error code if present, 0 if this is the
% innermost error.
%
% See Also: registererror
[~, errorid, errorstring, innercode] = errorcontainer('lookup', errorcode);
end
正如你所看到的,这个函数实际上只是错误容器的一个包装器,但是这样做可以使调用库的任何人保持整洁。
因此,让我们运行一个简单的示例。想象一个内部函数失败,并且 registererror 是这样调用的:
result = registererror('SOMEFUNC:SOMETASK', 'Something terrible happened.')
result = -2
调用函数注意到结果不是 0,而是一个负错误代码,并且它本身会抛出一个错误:
result = registererror('CALLINGFUNC:TOPTASK', 'Trying to do some high level thing failed.', result);
result = -3
这个值 -3 由我们的库返回给调用代码,因此它知道发生错误只是因为结果是否定的。然后它可以调用 errorcode2string 以获取有关错误的更多信息。
[errorid, errorstring, innercode] = errorcode2string(int32(-3))
errorid = CALLINGFUNC:TOPTASK
errorstring = Trying to do some high level thing failed.
innercode = -2
由于 innercode 仍然是负数,如果调用程序如此倾向于,它可以调用 errorcode2string 以在功能堆栈中查找更多信息。真的,这就是您找出发生的根本错误的方式。
[errorid, errorstring, innercode] = errorcode2string(int32(-2))
errorid = SOMEFUNC:SOMETASK
errorstring = Something terrible happened.
innercode = 0
现在内码为 0,所以我们知道我们可以停止。
该系统在我的项目中运行良好,希望对您有所帮助。这是我使用 Coder 时首先要弄清楚的事情之一,它教会了我很多关于 MATLAB Coder 中良好架构实践的知识,包括如何制作可全局访问的数据结构。