Matlab错误处理转换为C语言

Matlab错误处理转换为C语言,matlab,architecture,enums,matlab-coder,Matlab,Architecture,Enums,Matlab Coder,我想用面向对象的设计来编写一个Matlab代码,然后把它转换成C语言,最后它应该用在DSP处理器上 我不确定的是如何进行错误处理。据我所知,在C语言中应该使用enum,如果我在我的matlab代码中使用exception,我不确定matlab将如何将其转换为C语言 另一种可能性是实现枚举类,并使用它代替异常 因为我在软件架构方面没有太多经验,所以任何建议都是非常受欢迎的。这是一个相当老的问题,但我认为这是一个相当重要的问题,因为它涉及到一些基本问题,即如何最好地使用MATLAB编码器和构建代码

我想用面向对象的设计来编写一个Matlab代码,然后把它转换成C语言,最后它应该用在DSP处理器上

我不确定的是如何进行错误处理。据我所知,在C语言中应该使用enum,如果我在我的matlab代码中使用exception,我不确定matlab将如何将其转换为C语言

另一种可能性是实现枚举类,并使用它代替异常


因为我在软件架构方面没有太多经验,所以任何建议都是非常受欢迎的。

这是一个相当老的问题,但我认为这是一个相当重要的问题,因为它涉及到一些基本问题,即如何最好地使用MATLAB编码器和构建代码

一个好的错误处理系统对于任何中等大小的代码库都是非常重要的,因此对其进行结构化非常重要。在针对MATLAB编码器的代码中,您不能使用任何正常的异常函数(try、catch、error等),甚至“assert”在编码器中也有特殊意义。这意味着您需要自己创建一个良好的错误处理系统。这是不幸的,但并非不可逾越

您可以创建一个具有不同错误代码的枚举类型,然后返回相应的枚举。这是一种非常标准的C语言处理方式,它可以非常清晰地从MATLAB转换为C语言。它有几个缺点:

  • 必须维护枚举类以添加每个错误条件
  • 它不会直接促进堆栈跟踪
  • 同一个错误很可能会在不同的地方重复使用,这不允许对错误位置进行明确的跟踪
  • 它不提供人类可读的错误语句
  • 它不允许返回运行时数据,例如在底层函数中使用的可能导致错误的变量值
  • 这些都是内置的MATLAB系统提供的东西,所以拥有其中一些东西会很好

    在我的代码库中,我开发了一个服务于我的错误系统。它使用编码器中的一些强大工具来创建一个干净、易于使用的错误系统,看起来有点像MATLAB自己的错误系统。它还提供了与程序中错误处理的良好连接,该程序最终调用编码器生成的库

    第一个函数是errorcontainer。这是一个包含存储运行时错误的持久变量的函数

    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
    
    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
    ...
    
    result = registererror('SOMEFUNC:SOMETASK', 'Something terrible happened.')
    result = -2
    
    在人类可读的字符串中,我可以从实际执行中附加字符串数据。在本例中,我附加了理论函数中失败的文件名。我将所有函数的返回值设为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
    ,顾名思义,它可以获取一个错误代码并返回两个字符串,以及与错误相关的任何内部错误代码。以下是ErrorCode2字符串:

    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
    
    正如您所看到的,这个函数实际上只是errorcontainer的一个包装器,但是这样做可以为任何调用库的人保持整洁

    例子: 让我们运行一个快速示例。假设一个内部函数失败,调用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
    ...
    
    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
    
    现在innercode为0,所以我们知道可以停止


    这个系统在我的项目中运行得很好,我希望它能有所帮助。这是我使用Coder首先要解决的问题之一,它教会了我很多关于MATLAB Coder中的良好体系结构实践,包括如何使数据结构具有全局可访问性

    使用MATLAB编码器重新标记,该编码器用于C代码生成,而不是MATLAB编译器或部署产品。