3

C言語で簡単なインターフェースパターンを実現しようと考えています。

重要な特徴は、インターフェイスのパブリック ヘッダーによって提供される不透明な構造体に複数の定義を提供することです。つまり、実装が異なれば、その構造体に異なる基盤となるデータが提供されます (したがって、異なる翻訳単位間では、同じ構造体が異なる実装を持つことになります)。

これが良いデザインパターンなのか悪いデ​​ザインパターンなのか、参考になるものは見つかりませんでした。少なくとも、C標準で保証されている共通の最初の要素の一貫した順序とパディングにのみ依存しており、厳密なエイリアシングルールに違反していないようです(私が見る限り)。もちろん、C でオブジェクト指向のパターンがあることは知っていますが、この特定のパターンが使用されていることはわかりませんでした。

これは受け入れられますか?そのようなパターンも使用されましたか?(何も見つかりませんでした)

理解を容易にするために、以下に 3 つのソース ファイルの実際の例を示します。

reader.h (パブリック インターフェイスの定義)

#ifndef READER_H
#define READER_H

typedef struct reader_s reader_t;

char reader_read(reader_t* r);

#endif

reader.c (インターフェイスのグルー ロジック)

#include "reader.h"

typedef char (reader_f)(reader_t*);

struct reader_s{
 reader_f* rfunc;
};

char reader_read(reader_t* r)
{
 return r->rfunc(r);
}

reader1.h (インターフェース、ヘッダーの実装)

#ifndef READER1_H
#define READER1_H

#include "reader.h"

reader_t* reader1_get(void);

#endif

reader1.c (インターフェースの実装、コード)

#include "reader1.h"

typedef char (reader_f)(reader_t*);

struct reader_s{
 reader_f* rfunc;
 int       pos;
};

static char reader1_read(reader_t* r);

reader_t reader1_obj = {&reader1_read, 0};

reader_t* reader1_get(void)
{
 return &reader1_obj;
}

static char reader1_read(reader_t* r)
{
 char rval = 'A' + (r->pos);
 (r->pos) = (r->pos) + 1;
 if ((r->pos) == 24){ (r->pos) = 0; }
 return rval;
}

main.c (使用例)

#include "reader1.h"
#include <stdio.h>

int main(void)
{
 reader_t* rd = reader1_get();
 int       i;

 printf("\n");
 for (i = 0; i < 60; i++){
  printf("%c", reader_read(rd));
 }
 printf("\n");

 return 0;
}

おそらく、インターフェイスは実装者に別のヘッダー ファイルを提供し、関数ポインターを含む構造体 head の定義を提供する必要があります。多くのメソッドを持つクラスのオブジェクト サイズを最小化するために、それらを含むクラス構造を指すことさえあるかもしれません。

4

1 に答える 1

1

C 標準では、このような場合の動作を定義するものは何もありませんが、実装でデータ型の格納形式とモジュール間呼び出しを処理する手段が指定されている場合、その結果として動作を定義できます。ただし、「リンク時の最適化」を伴う一部の実装では、文書化された「通常の」方法でモジュール間呼び出しを常に実行するとは限らないことに注意してください。

このようなアプローチが実行可能であることがわかる唯一の場合は、型に関連付けられたパブリック API が、コードが型のインスタンスを直接宣言するか、代入演算子を使用できるようにしたいが、そうでなければコミットしたくない場合です。他の目的のためにフィールド レイアウトを公開します。uint32[]その場合、 orのようなものを含む public 構造型を持つuint64[]型の内部を処理するすべてのコードが「実際の」構造型を使用したとしても、合理的なアプローチである可能性があります。このような設計により、サイズが一定のままであれば、内部構造の型が変更された場合にクライアント コードを再コンパイルする必要がなくなります。一方で、gcc スタイルの型ベースのエイリアシングの「最適化」を有効にして処理する必要があるコードのそのような設計は考慮しません。なぜなら、gcc はそのような最適化がコードを壊す場所を意図的に隠しているからです。

于 2017-04-12T22:26:08.150 に答える