3

まず、この質問があまりにもオープンエンドでないことを願っています。コーディング標準/ポリシー、ライブラリ/API の比較などで使用するための要素として、形容詞「ライブラリセーフ」の正式または少なくとも半正式な定義を確立しようとしています。定義の一部としてベスト プラクティスについての私自身の見解やその他の恣意的な見解を押し付けないようにするために、過度に制限的な定義は避けたいと思います。私が言いたいことの例として、静的ストレージ期間の非 const 修飾オブジェクトにアクセスするコード、または他のプロセスグローバル状態 (シグナル処理など) を変更するコードをライブラリセーフでないと見なすことは、おそらく実用的な定義ですが、必要以上に厳格です。

私が捉えようとしている重要なプロパティは、コードを「ライブラリセーフ」と言うときに理解されると思いますが、次のとおりです。

  1. ライブラリ コードを使用する同じコードの複数のインスタンス (再帰またはスレッド) を意味するか、ライブラリ コードを使用するプログラムの完全に分離した部分を意味するかに関係なく、同じプロセスでライブラリ コードの複数の「ユーザー」を持つことができます。これらのユーザーは必要ありません。お互いのつま先を踏む。

  2. インターフェイス コントラクトに記載されている場合を除き、呼び出し元に「属する」状態を変更しない。

私がこの質問に適していると考える回答の種類は、この概念を定義するための同様の過去の試みへの言及、定義をまとめるための強力なアイデア、またはこの概念を正確に定義しようとする試みは無駄であるという説得力のある議論です.

ところで、この C、C++、および POSIX にタグを付けたのは、これらのコンテキストにそのような定義を適用することに最も関心があるためです。他の言語のコンテキストではあまり面白くないかもしれません。たとえば、非常に純粋な関数型言語では、すべてのコードはおそらく「ライブラリセーフ」と見なされるべきです。


提案された定義:

次のようなプログラム A と B が存在する場合、ライブラリ L はライブラリ安全ではありません。

  • A と B は入力を受け付けません。
  • A と B は、終了ステータス以外の出力を生成しません。
  • A と B は、未定義の動作を呼び出しません。
  • A と B には、グローバル状態を変更する L 以外のコードは含まれていません。
  • A と B を 1 つのプログラムに結合し、必要に応じて L 以外の関数またはオブジェクトの名前を変更して衝突を回避し、A と B のメイン関数がそれぞれ独自のスレッドで実行されるようにすると、プログラムは未定義の動作を呼び出したり、出力が異なったりします。 A と B を別々に実行した出力から。
4

2 に答える 2

0

ACSLは C 関数の仕様言語です。これは、Design-by-Contract の哲学に従います。ただし、実行時のアサーションにコンパイルすることは意図されていません。これは、契約による設計に着想を得た他のフレームワークで使用する前条件と事後条件を配置する最も一般的な方法です。

ACSLは個々の関数が何をするかを指定するためのものですが、ACSLは個々の関数が何をするかを指定するためのものですが、私が正しく理解していれば、あなたが求めているのは、適切なライブラリ関数が何をするかについての一般的な概念のためです。しかし、私はこの2つが一緒に行かなければならないと思います。「適切なライブラリ関数」の唯一の定義は、「それが言うことを実行する関数」です。一般的ではないものは制限が多すぎます。例として、適切なライブラリ関数は一般にメモリの割り当てを解除すべきではありません…メモリfree()の割り当て解除として指定されているか、指定されていない限り。


したがって、少なくとも、ACSL が予測するものと一部の ACSL 句のデフォルトを調べると、特に指定がない限り、適切なライブラリ関数が持つべきプロパティを示唆できます。

  • ACSL コントラクトは、事前条件で関数の期待を記述し、事後条件でそれが提供する保証を記述します。
  • ACSL コントラクトは、事前条件を満たすすべての入力に対して関数が終了することを意味しますが、それ以外を指定することは可能です。関数が終了しない 2 つの方法は、永久にループするか、プログラムを終了させることです。
  • 関数に渡されるメモリ位置は、事前条件で特に指定されていない限り、エイリアスを許可されます。
  • ACSL コントラクトは、関数が変更する可能性があるすべてのメモリ位置を記述します。リストされていないメモリ位置は、関数の呼び出しによって変更できません。
  • オプションで、各出力の計算のためにどのメモリ位置が読み取られるかを記述します。メモリ状態に配置された関数への 2 つの呼び出しは、この読み取りメモリ ロケーションのリスト以外でのみ異なるものであり、同じメモリ ロケーションを変更し、それらを同じ値に設定します。
  • 関数呼び出し中に動的に割り当てまたは解放されるメモリ ブロックは、特定の ACSL 句にリストされます。デフォルトでは、関数はメモリを割り当てたり解放したりしません。

FPU 丸めモードの変更などの一部の副作用は、ACSL ではモデル化されませんが、グローバル変数の変更と見なすことができます。その他の副作用 (スレッドの作成、シグナル ハンドラーのインストール) は、ACSL の範囲外の C の側面を参照します。

ACSL コントラクトの例:

/*@ requires \valid(((char*)s)+(0..n - 1));
  @ assigns ((char*)s)[0..n - 1] \from c;
  @ assigns \result \from s;
  @ ensures \forall integer i; (0 <= i < n) ==> ((char*)s)[i] == (char) c;
  @ ensures \result == s;
  @*/
void *memset(void *s, int c, size_t n);

ACSL の実用的な使用とライブラリの実用的な非公式仕様の両方に関する最後の注意として、C 言語では、カプセル化と仕様の間にトレードオフがあります。特に、変数を変更するライブラリ関数の場合static、この変数は、呼び出し元の範囲外ですが、指定から省略できません。

などの関数random()は assign を使用しません\result \from \nothing。これは、2 つの連続した呼び出しが常に同じ結果を返すことを意味するためです。一方、 のような関数double sin(double x);は、引数が同じであるとすぐに同じ結果assigns \result \from x;を返すための 2 つの呼び出しsin()(FPU の丸めモードにもかかわらず。FPU の丸めモードがグローバル変数としてモデル化されている場合、この関数の入力になります。)

于 2013-04-28T07:22:40.183 に答える