1

ポリモーフィズムを使用してCでオブジェクト指向スタイルのプログラミングを行いたいと思います。ここで、インターフェイスクラスには関数のテーブルへのポインタが含まれています。次のような例:

/* Implement polymorphism in C, Linux kernel-style */
struct statement {
    const struct statement_ops *ops;
    struct list_head list;      /* when on master input list */
    void *private;          /* pointer to type-specific data */
};

struct statement_ops {
    int (*analyse)(void *private, int pc);
    int (*get_binary_size)(void *private);
};

void user(void)
{
    struct statement *s = make_a_statement();
    if (s->ops->analyse(s->private, foo))
        blah blah;
}

すべての「メソッド」にs->privateを明示的に渡さずに何かを記述できるようにしたいと思います。何か案は?いくつかのマクロトリックかもしれませんか?

4

3 に答える 3

6

これがパブリックインターフェイスの一部である場合は、アクセサ関数を追加できます。隠れた利点は、アクセサで健全性チェックやその他の作業を実行できることです。(「object」のように「this」ポインタを「o」と呼んでいることに注意してください。一貫性を保つために、このようにしたほうがいいです。)

int statement_analyse (struct statement *o, int pc)
{
    assert(pc >= 0);

    int ret = o->ops->analyse(o->private, pc);
    assert(ret >= 0);

    return ret;
}

これで、「プライベート」を明示的に渡さなくてもこれを呼び出すことができます。

void user(void)
{
    struct statement *s = make_a_statement();

    if (statement_analyse(s, foo))
        blah blah;
}

これにはメリットがないように思われるかもしれませんが、アクセサーを実装する必要があるため、明確に定義された堅牢なインターフェイスが必要であると仮定すると、アクセサー関数はアサーションとインターフェイスのドキュメントを配置する唯一の適切な場所です。実際、適切なアサーションを作成すると、アサーション自体がインターフェースの文書化に役立ちます。また、アクセサーに健全性チェックを追加すると、アクセサーが呼び出す実際のメソッドにそれらを追加する必要はありません。

もちろん、このアプローチは、関数ポインターを介して呼び出される関数がユーザーによって提供されるものである場合、または他の方法で別のものである場合にのみ意味があります。analyse()常に同じことを行う単一のメソッドがある場合は、statement_analyse()必要なことを直接実行するを実装するだけです。

小さな注意:OOPを実行するときは、構造体をtypedefして、CamelCase名を付けることを好みます。この規則は、構造体が不透明であり、パブリックインターフェイスを介してのみアクセスする必要があることを示す方法として使用します。主観的ですが、見た目も良くなります。また、コンストラクターがそれをmallocするのではなく、ユーザーに構造体自体にメモリを割り当てさせることを好みます。これにより、mallocの失敗を処理する必要がなくなり、プログラムが少し効率的になります。

typedef struct {
    ...
} Statement;

void Statement_Init (Statement *o);
int Statement_Analyse (Statement *o, int pc);
于 2012-04-22T09:04:28.973 に答える
3

self残念ながら、またはオブジェクトの受け渡しを許可するメソッドを作成することがthis、Cでこれを実現する唯一の方法です。

マクロトリックを使用してその一部を非表示にすることができますが、その時点では、実際にはCではなくなります。

于 2012-04-22T08:59:53.860 に答える
2

他の回答が言うように、適切なポインターで関数を呼び出さずにこれを行う方法はありませんが、(Williham Totlandが示唆するように)マクロを使用して呼び出しを合理化できます(可変個引数マクロをサポートするコンパイラが必要です)。

// macro_call.c

#define C_ARGS(stmnt, func, ...) (stmnt)->ops->func((stmnt)->private, ...)
#define C_NOARGS(stmnt, func) (stmnt)->ops->func((stmnt)->private)

C_ARGS(s, analyse, 1);

C_ARGS(s, lots_of_args, 1, 2, 3, 4);
C_NOARGS(s, no_args);

(これCは「呼び出し」用です。)

その上で(を介してgcc -E macro_call.c)前処理を行うと、次のようになります。

(s)->ops->analyse((s)->private, 1);

(s)->ops->lots_of_args((s)->private, 1, 2, 3, 4);
(s)->ops->no_args((s)->private);

これは、アクセサ関数バージョンに似ています。マクロバージョンは、いくつかの点でわずかに柔軟性がありますが、安全性が低く、微妙なエラーや間違いにつながる可能性があります。

に追加の引数を渡さないC_ARGSと結果が生じるため、2つのマクロがありs->ops->func(s->private, )ます。これを修正することは可能だと思いますが、厄介で、かなり多くのコードが必要になります(空の場合__VA_ARGS__は処理が難しいことで有名です)。

于 2012-04-22T09:42:23.710 に答える