2

Matlabコードにオブジェクト指向設計を使用し、後でそれをCに変換することを考えました。最終的にはDSP-プロセッサに使用する必要があります。

よくわからないのは、エラー処理の方法です。私の知る限り、Cではenumを使用する必要があり、matlabコードで例外を使用する場合、matlabがそれをCに変換する方法がわかりません.

別の可能性は、列挙型クラスを実装し、例外の代わりに使用することです。

私はソフトウェア アーキテクチャの経験があまりないので、どんな提案でも大歓迎です。

4

1 に答える 1

2

これはかなり古い質問ですが、MATLAB Coder を最適に使用してコードを設計する方法に関するいくつかの基本的な問題になるため、非常に重要な質問だと思います。

適切なエラー処理システムは、適度なサイズのコードベースにとって非常に重要であるため、構造化することが重要です。MATLAB Coder を対象とするコードでは、通常の例外関数 (try、catch、error など) を使用することはできず、Coder では「assert」でさえ特別な意味を持ちます。つまり、適切なエラー処理システムを自分で作成する必要があります。これは残念なことですが、克服できないわけではありません。

さまざまなエラー コードで列挙型を作成し、適切な列挙型を返すことができます。これは非常に標準的な C の方法であり、MATLAB から C に非常にきれいに変換されることは明らかです。いくつかの欠点があります。

  1. すべてのエラー条件を追加するには、enum クラスを維持する必要があります。
  2. スタック トレースを直接促進するわけではありません。
  3. 同じエラーが別の場所で再利用される可能性が高いため、エラーの場所を明確に追跡することはできません。
  4. 人間が読めるエラーステートメントは提供しません。
  5. たとえば、エラーの原因となった可能性のある最下位レベルの関数で使用されていた変数値など、ランタイム データを返すことはできません。

これらはすべて組み込みの MATLAB システムが提供するものなので、それらのいくつかがあれば便利です。

私のコードベースでは、うまく機能するエラー システムを開発しました。Coder のいくつかの強力なツールを使用して、MATLAB 独自のエラー システムに少し似た、クリーンで使いやすいエラー システムを作成します。また、Coder によって作成されたライブラリを最終的に呼び出すプログラムのエラー処理への適切な接続も提供します。

最初の関数は 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

この関数の中心は、実行するタスクの選択を可能にする大きな 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) の値はエラーがなかったことを意味し、負の値はこれらの登録されたエラー識別子の 1 つです。

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 への追加のオプションの呼び出しがあります。便利な方法の 1 つは、エラーのスタックを作成することです。より深い関数のエラーが問題を引き起こしたことがあり、完全なスタック トレースが必要です。内部エラー 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 呼び出しの 3 番目の引数として追加され、さらに別のエラー値を返します。この値で何ができるでしょうか。

ライブラリを使用してコードから呼び出すことができる 3 番目の関数が必要です。この関数は と呼ばれerrorcode2string、その名前が示すように、エラー コードの 1 つを取り、2 つの文字列と、エラーに関連する内部エラー コードを返すことができます。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

内部コードは依然として負であるため、呼び出し元のプログラムがその傾向にある場合は、errorcode2string を呼び出して、機能スタックの下にある詳細情報を見つけることができます。本当に、これが発生した根本的なエラーを見つける方法です。

[errorid, errorstring, innercode] = errorcode2string(int32(-2))
errorid = SOMEFUNC:SOMETASK
errorstring = Something terrible happened.
innercode = 0

これで内部コードが 0 になったので、停止できることがわかりました。

このシステムは私のプロジェクトでうまく機能しており、お役に立てば幸いです。これは、Coder を使用するために最初に理解しなければならなかったことの 1 つであり、グローバルにアクセス可能なデータ構造を作成する方法など、MATLAB Coder の優れたアーキテクチャ プラクティスについて多くのことを学びました。

于 2015-04-16T02:52:24.330 に答える