4

バックグラウンド:

新しい組み込みシステムのファームウェアをモデル化しています。現在、ファームウェアはUMLでモデル化されていますが、UMLモデリングツールのコード生成機能は使用されません。

ターゲット言語はC(具体的にはC99)になります。

低電力(つまり、パフォーマンス、迅速な実行)と正確さは重要ですが、コードサイズや実行速度など、何よりも正確さが最優先事項です。

システムのモデリングでは、明確に定義されたコンポーネントのセットを特定しました。各コンポーネントには独自のインターフェースがあり、多くのコンポーネントが多くのコンポーネントと相互作用します。

モデル内のほとんどのコンポーネントは、リアルタイムオペレーティングシステム(RTOS)での個別のタスク(スレッド)になりますが、一部のコンポーネントはライブラリにすぎません。タスクは、メッセージパッシング/キュー投稿を介して完全に相互に通信します。ライブラリとの相互作用は、同期関数呼び出しの形式になります。

アドバイス/推奨事項は規模によって異なる場合があるため、いくつかの情報を提供します。現在、約12〜15個のコンポーネントがありますが、最大20個に増える可能性がありますか?数百のコンポーネントではありません。平均して、各コンポーネントが他のコンポーネントの25%と相互作用するとします。

コンポーネント図には、コンポーネント間のインターフェイスを表すために使用されるポート/コネクタがあります。つまり、一方のコンポーネントが他方のコンポーネントに必要なものを提供します。ここまでは順調ですね。

ここにこすりがあります。「コンポーネントA」が「コンポーネントB」のすべてのインターフェースにアクセスできないようにする場合が多くあります。つまり、コンポーネントAをコンポーネントBが提供するインターフェースのサブセットに制限したい場合があります。

質問/問題:

コンポーネント図で定義されたインターフェイスコントラクトを(できればコンパイル時に)強制するための体系的でかなり簡単な方法はありますか?

明らかに、コンパイル時のソリューションは実行時のソリューションよりも望ましいです(より早い検出、より良いパフォーマンス、おそらくより小さなコード)。

たとえば、ライブラリコンポーネント「B」が関数X()、Y()、およびZ()を提供するとしますが、コンポーネント「A」が関数Z()のみを呼び出し、X()およびY()を呼び出せないようにします。 。同様に、コンポーネント「A」は、メッセージキューを介して多数の異なるメッセージを受信して​​処理できる場合でも、メッセージを任意のコンポーネントに送信できるコンポーネントはありません。

私が思いついた最善の方法は、コンポーネントとコンポーネントのインターフェイスごとに異なるヘッダーファイルを用意し、コンポーネントが使用を許可されているインターフェイスの部分のみを(ヘッダーファイルを介して)公開することです。明らかに、これにより多くのヘッダーファイルが作成される可能性があります。これは、コンポーネント間でのメッセージパッシングがOS APIで直接行われるのではなく、それぞれが特定の(許可された)メッセージを作成して送信する関数呼び出しを介して行われることも意味します。同期呼び出し/ライブラリの場合、APIの許可されたサブセットのみが公開されます。

この演習では、人々が行儀が良いと想定できます。 言い換えれば、関数プロトタイプを直接不正行為、切り取り、貼り付けしたり、許可されていないヘッダーファイルをインクルードしたりすることを心配する必要はありません。許可されていない場合、「A」から「B」へのメッセージを直接投稿することはありません。

コンパイル時のアサーションを使用してコントラクトを適用する方法があるかもしれません。オーバーヘッドが発生したとしても、実行時にこれをチェック/実施するためのより洗練された方法があるかもしれません。

コードはクリーンにコンパイルおよびリントする必要があるため、「関数プロトタイプファイアウォール」アプローチは問題ありませんが、これを行うにはもっと慣用的な方法があるようです。

4

3 に答える 3

2

ヘッダーの考え方は正しいですが、コンポーネント間のインターレースによっては、各コンポーネントにヘッダーファイルを提供するのではなく、各コンポーネントのインターフェイスを独自のヘッダーファイルを持ついくつかのサブカテゴリに分割する方がクリーンな場合があります。 -コンポーネント接続。

サブカテゴリは必ずしも互いに素である必要はありませんが、(プリプロセッサディレクティブを介して)再定義を取得せずにカテゴリを混在できることを確認してください。これは、独自のインクルージョンガードを使用して各タイプまたは関数宣言のヘッダーファイルを作成し、これらのアトミックブロックからサブカテゴリヘッダーを構築することにより、体系的な方法で実現できます。

于 2010-10-26T20:27:15.140 に答える
2
#ifdef FOO_H_

   /* I considered allowing you to include this multiple times (probably indirectly)
      and have a new set of `#define`s switched on each time, but the interaction
      between that and the FOO_H_ got confusing. I don't doubt that there is a good
      way to accomplish that, but I decided not to worry with it right now. */

#warn foo.h included more than one time

#else /* FOO_H_ */

#include <message.h>

#ifdef FOO_COMPONENT_A

int foo_func1(int x);
static inline int foo_func2(message_t * msg) {
    return msg_send(foo, msg);
}
...

#else /* FOO_COMPONENT_A */

  /* Doing this will hopefully cause your compiler to spit out a message with
     an error that will provide a hint as to why using this function name is
     wrong. You might want to play around with your compiler (and maybe a few
     others) to see if there is a better illegal code for the body of the
     macros. */
#define foo_func1(x) ("foo_func1"=NULL)
#define foo_func2(x) ("foo_func2"=NULL)

...
#endif /* FOO_COMPONENT_A */

#ifdef FOO_COMPONENT_B

int foo_func3(int x);

#else /* FOO_COMPONENT_B */

#define foo_func3(x) ("foo_func3"=NULL)

#endif /* FOO_COMPONENT_B */
于 2010-10-26T21:14:38.890 に答える
1

nategooseが彼の回答で提案した内容に沿ってヘッダーファイルを生成するためのミニ言語とシンプルなツールの作成を検討する必要があります。

その回答でヘッダーを生成するには、次のようにします(これを呼び出しますfoo.comp)。

[COMPONENT_A]
int foo_func1(int x);
static inline int foo_func2(message_t * msg) {
    return msg_send(foo, msg);
}

[COMPONENT_B]
int foo_func3(int x);

(そして、複数のコンポーネントで使用可能なインターフェースを提供するために例を拡張します):

[COMPONENT_B, COMPONENT_C]
int foo_func4(void);

これは、ヘッダーファイルを解析して生成するのが簡単です。インターフェイス(特にメッセージパッシングの可能性があると思われる)が上記で想定したよりもさらに定型的である場合は、言語をいくらか単純化できます。

ここでの利点は次のとおりです。

  1. メンテナンスを容易にするための少しの構文糖衣。
  2. 後でより良い方法を見つけた場合は、ツールを変更することで保護スキームを変更できます。変更する場所が少なくなるため、変更できる可能性が高くなります。(たとえば、nategooseが提案する「違法なマクロコード」の代わりを後で見つけるかもしれません。)
于 2010-10-26T21:37:43.780 に答える