安全 SIL 4 システムの多数の状態遷移を持つ複雑なステート マシンを実装しました。この実装のバックボーンは、関数ポインターを使用して行われました。すべてが順調に進んでいたとき、V&V は SIL 4 システムでの関数ポインターの使用に反対しました。参照-ルール 9 NASA .Misra C 2004 では、関数ポインタを使用できないとは述べていません。
関数ポインターを使用せずに複雑なステート マシンを実装する他の方法はありますか?
安全 SIL 4 システムの多数の状態遷移を持つ複雑なステート マシンを実装しました。この実装のバックボーンは、関数ポインターを使用して行われました。すべてが順調に進んでいたとき、V&V は SIL 4 システムでの関数ポインターの使用に反対しました。参照-ルール 9 NASA .Misra C 2004 では、関数ポインタを使用できないとは述べていません。
関数ポインターを使用せずに複雑なステート マシンを実装する他の方法はありますか?
まず第一に、NASA の文書は正典ではありません。NASAの文書に従うことを強制する法律/指令/基準/要件/文書を尋ねることから始めます. それがどこにも強制されていない場合 (NASA 自体でさえ、非常に可能性が高いようです)、それに従う義務はなく、すべてを却下することができます。
ナンセンスをナンセンスとして却下できなければ、安全基準の壁にぶつかったときの通常の手順を使用できます。解決策は、規定されたルールがどのように意味をなさないかを詳細に文書化し、独自の方法論でそれらを平手打ちすることです。 .
そのため、関数ポインタを放棄するのではなく、以下で説明する方法を使用して安全な方法で使用するようにしてください。
すべての安全関連の設計はリスク評価に要約されるため、次のことが常に得られます。
エラー→原因→危険→安全対策
NASA 文書からの与えられた (不十分な) 論理的根拠を使用すると、次のような方法で「関数ポインターを回避する」安全対策を正当化できます。
間違ったコードが実行された -> 破損した関数ポインタ -> ランナウェイ コード/不正な op コード
スタック オーバーフロー -> 関数ポインタの再帰 -> メモリ破損
混乱したプログラマー -> 関数ポインターの構文 -> 意図しないプログラム機能
それはすべてかなり曖昧で疑わしいリスク評価ですが、これがNASAの文書が要約するものです.
上記の 3 つの危険に対して「関数ポインターを回避する」代わりに、次の安全対策を使用することをお勧めします。
- 防御的プログラミングとアサーション。
- 防御的プログラミングとアサーション。プログラマーを教育します。
- typedef を使用します。プログラマーを教育します。
防御的プログラミングとアサーション
STATES_N
ものなど) を静的にアサートしsizeof(func_pointer_array)/sizeof(*func_pointer_array)
ます。STATE_MACHINE[i]();
ステート マシンの呼び出しがwhereSTATE_MACHINE
が関数ポインターの配列のように見える場合、実行時チェックを追加i
して常に有効であることを確認します。const
可能であればポインターを使用します (ポインター自体は読み取り専用です)。実行時にそれらを再割り当てする必要がある場合は、それらを呼び出す前に有効な関数を指していることを確認してください。上記の種類のステート マシンは慣用的で非常に安全であり、コード内の他の場所での通常の関数呼び出しよりもはるかに安全です。もちろん、状態遷移が安全かつ合理的な方法で行われるようにする必要がありますが、それは関数ポインターに関するものではありません。
再帰の回避
これは主に、関数ポインターを使用しないように、または関数ポインターを使用しないようにプログラマーに教育することです (これにより、Toyota のバグを防ぐことができたようです)。
再帰を発見することも回避することも難しくないため、ある程度まともなコード レビュー手続きを行えば、再帰を防ぐのに十分なはずです。ベテランの組み込みシステム プログラマーは、安全性が重要なシステムの経験に関係なく、再帰を含むコードを承認しません。
すべての安全関連コードは、セーフティ クリティカルなプログラム設計のn年の経験を持つベテラン C プログラマーによってレビューおよび承認される必要があることを示す社内設計ルールを設定できます/設定する必要があります。
さらに、静的アナライザー ツールを使用して再帰をチェックする必要もあります (関数ポインターを介して再帰を検出できない場合でも)。MISRA-C の任意のバージョンに準拠する静的アナライザーをお持ちの場合は、これが含まれています。
意図しない再帰に関しては、上記の防御的なプログラミング方法で回避されます。
紛らわしい関数ポインタの構文
C の関数ポインターの構文は、確かに非常に紛らわしい場合があります。
int (*(*func)[5])(void);
または他のばかげた例。typedef
これは、常に関数ポインター型を強制することで解決できます。
(参照: Les Hatton, Safer C , p184 「安全関連の観点から言えば、単純な答えは、typedef メカニズムの外では決して許可されるべきではないということです。」)
それらを型定義するには2つの異なる方法がありますが、私はこれを好みます:
typedef int func_t (void);
func_t* fptr;
これは、一般的に悪い習慣である typedef の背後にあるポインターを非表示にしないためです。しかし、代替案の方が快適だと感じる場合は
typedef int (*func_t) (void);
func_t fptr;
それなら練習もOK。