2012-04-22 14 views
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をすべての「メソッド」に渡​​すことなく何かを書くことができるようにしたいと考えています。何か案は?おそらくいくつかのマクロトリックですか?

答えて

6

これは、パブリックインターフェイスの一部である場合、あなたはアクセサ関数を追加することができます。隠された利点は、アクセサーで健全性チェックや他の作業を行うことができることです。 (「オブジェクト」のように。私はそれを一貫性のためにその方法を好む、私は「この」ポインタ「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をし、それらにキャメルケースの名前を与えることを好みます。私は構造体が不透明であることを伝える方法としてこの規約を使用し、パブリックインターフェイスを介してのみアクセスする必要があります。それはまた、主観的ですが、よりよく見えます。私はまた、コンストラクタmalloc'ingとは対照的に、ユーザーが構造体自体にメモリを割り当てることを好む。これにより、mallocの失敗を処理する必要がなくなり、プログラムがより効率的になります。

typedef struct { 
    ... 
} Statement; 

void Statement_Init (Statement *o); 
int Statement_Analyse (Statement *o, int pc); 
+0

かなり十分ですが、マクロラッピングの可能性があるほど良いと思います。私はCamelCaseへの個人的な嫌悪感を持っている:) – blueshift

3

は残念ながら、selfまたはthisオブジェクトの通過を許可するようにメソッドを書くことがC.

でこれを達成するための唯一の方法ですあなたはそれの一部を非表示にするマクロのトリックを使用することができますが、その時点でそうではありません本当にCはもうありません。

2

他の回答が言うように(Williham Totlandのが示すように)、あなたは電話を合理化するためのマクロを使用することができますが、適切なポインタで関数を呼び出すことなくこれを行うには方法はありませんが、は(variadic macroとコンパイラが必要ですサポート):。

// 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に余分な引数を渡すないことs->ops->func(s->private,)につながるので

二つのマクロがありますが、私はこの問題を解決することは可能だと思うが、それは厄介で、(空__VA_ARGS__に対処するために悪名高くにくい)かなり多くのコードが必要になります。

+0

私はこれのような何かを驚いていた。私は '#define DBG(fmt、args ...)printf(fmt、## args)'のようなマクロを使うことがよくあります。 – blueshift

関連する問題