8

堅牢なCプログラムでこのようにするのは良い/正しい方法ですか

//File1 => Module1.h

static int Fun(int);

struct{
int (*pFn)(int)
}Interface;

//File2 => Module1.c

static int Fun(int){
//do something
}

Interface* Init(void)
{
  Interface *pInterface = malloc(sizeof(Interface));
  pInterface->pFn = Fun;
  return pInterface;
}

//File 3 => Module2.c
#include"Module1.h"
main()
{
  Interface *pInterface1 = Init();
  pInterface1->pFn(5);
}

私の意図は、各モジュールがインターフェースを公開することです...

質問:

  1. インターフェイスを公開するために、上記のような C コードを記述してもよろしいでしょうか??
  2. インターフェイスを公開するためのより良い方法は何ですか??
  3. C プログラミング (C++ ではない) の設計原則に関するリファレンスはありますか??
4

3 に答える 3

1

これは、動的にロードされたモジュールではより慣用的です。(または、これらの線に沿ったもの。)

通常、2 つのソース ファイル間のインターフェイスは、extern直接アクセスされる関数とオブジェクトを使用して定義されます。より洗練された何かを行うには、主張する必要があります。

于 2013-10-11T05:34:18.980 に答える
0

私の短い答えはノーです。

static 関数を .h ファイルで宣言してはならないことを指摘するコメント (非表示にする必要がある部分です。これは結局のところ static です) を無視して、次の点に注意してください。

モジュールの消費者から実装を分離するために、インターフェイスを使用しようとしているようです。高貴な目標。これにより、呼び出しコードを壊すことなく .c ファイルで実装を変更できるため、柔軟性が向上します。ただし、定義したインターフェースについて考えてみてください。

このモジュールを使用するすべてのコードは、Init を介して Interface インスタンスを取得できることが保証されている必要があります (ただし、これは .h ファイルに含める必要があります)。その Interface インスタンスは、使用する関数を指します。そのような関数の 1 つに署名があります。

int Fun(int);

それで、あなたは何を隠しましたか?もちろん、呼び出している関数を変更することはできますが、そのシグネチャを持つ関数を提供する必要があります。

または、.h を次のように定義することもできます。

int Fun(int);

そして、.c ファイルでは、次のようになります。

static int StaticFun(int i)
{
    // something
}

int Fun(int i)
{
    StaticFun(i);
}

Fun が管理する内部状態や構成ファイルに基づいて、Fun に別の関数を呼び出させることができます。なぜこれが良いのですか?単純であることに加えて、これは静的呼び出しです。たとえば、コンパイラは StaticFun への呼び出し、または関数 StaticFun を完全にインライン化できます。関数ポインターを介した呼び出しは、通常、顕著なパフォーマンス ヒットを引き起こします。

ここで、定義したような「インターフェイス」オブジェクトの場合があります。しかし、あなたの場合、変更なしではなく、そうではないことを示唆したいと思います。

Init関数が次のように変更されたとします。

Interface* Init(some state info);

返される Interface オブジェクトは、渡された状態または構成に応じて変更できるようになりました。これにより、モジュールは関数を動的にマップできます。基本的に、私の意見では、実際にその下で静的関数を呼び出している場合、必要以上に多くの作業を行っており、コンパイラの最適化を妨げています。

注意として、インターフェース ルートに進む場合 (私は現在、似たようなプロジェクトに取り組んでいます)、変更を提案してもよろしいですか? 何かのようなもの...

.h ファイル:

const Interface* Init(some state);

.c ファイル:

static int FunTyp1(int);
static int FunTyp2(int);


static const Interface typ1 = { &FunTyp1 };
static const Interface typ2 = { &FunTyp2 };

const Interface* Init(some state)
{
    if (some state == type1)
       return &typ1;
    else if (some state == type2)
       return &typ2;
}

利点:

  1. インターフェイスを変更するためにコードを使用したくないと思いませんか? 少なくとも、私には正当な理由が思い浮かびません。
  2. これを静的に定義することで、不必要なヒープ割り当てと、消費者が Interface オブジェクトを繰り返し要求し、それらを解放するのを忘れたときのメモリ リークを回避できます (または、解放すべきかどうかわからない - このオブジェクトの所有者は誰ですか?)。
  3. 誰かがそれを変更することを心配することなく、プロセス内のすべての消費コードに対して Interface オブジェクトの単一 (静的) インスタンスを共有できます。

いくつかの静的に初期化されたインスタンスを持つことで、サポートする関数マッピングごとに異なる「インターフェイス」オブジェクトを定義できることに注意してください。

于 2013-10-11T06:02:42.343 に答える