2013-03-31 5 views
7

を使用して動的ディスパッチ仮想メソッドテーブルを使用してC。例えばダイナミック発送は、私はCを勉強し、練習として、私はJavaからに翻訳したい <p>C.</p>で動的ディスパッチを実現するために(好ましくは良い例による)のヒントを見つけることを期待しています仮想メソッドテーブル

私はJavaのコードがあります。

abstract class Foo { 
    public abstract int val(); 
    public abstract Boolean error(); 
} 

class Fail extends Foo { 
    public int val(){ return 0;} 
    public Boolean error(){return true;} 
} 

class IntFoo extends Foo { 
    int v; 
    public IntFoo(int value){this.value=v;} 
    public int val(){ return v;} 
    public Boolean error(){return False;} 
} 

を、私はちょうどこのようないくつかの基本的なもの翻訳することができます:私はしませんので、私はこれを完了しようとしている間に立ち往生しています

typedef struct Foo{ 
    void(**vtable); 
}Foo; 

typedef struct Fail{ 
    void(**vtable); 
    struct Foo inherited; 
}Fail; 

typedef struct IntFoo{ 
    void(**vtable); 
    struct Foo inherited; 
}IntFoo; 

を知っている:

  1. これらのメソッドをcで定義する方法。
  2. vtableにこれらのメソッドのアドレスを設定して、コンパイラ が正しい呼び出し方法を認識できるようにします。
  3. それを機能させるために何を定義するか。
+0

円形のペグを正方形の穴に入れようとするのではなく、非OOP言語でOOP構造を強制しようとすると、私はC++を使用し、Cでは使用しません。 –

+3

@HovercraftFullOfEels私は私の教授が求めているように、私はそれをCで行うつもりです。 – Solix

+1

*この種のもののソース*は、[Axel Tobias Schreiner]の[ooc.pdf](http://www.cs.rit.edu/~ats/books/ooc.pdf)です(http:// www .cs.rit.edu /〜ats/books /)。 –

答えて

3

私がお勧めするアプローチは、ディスパッチテーブルを使用する他のいくつかのCコードを見て、どのように構造化されているかを見ることです。私が頭の上から知っている具体的な例(おそらく最良の教えの例ではなく、私が取り組んできたランダムなフリーソフトウェアなので)は、MIT KerberosとHeimdalです。両方ともディスパッチテーブルを使います場所とApache Webサーバー、および動的にロードされたモジュールをどのように処理するかについて説明します。いずれも継承を行いませんが、継承は比較的簡単に追加できます。呼び出すメソッドがNULLかどうかを確認し、そうであればそのメソッドの親ディスパッチテーブルをチェックします。

一般的にディスパッチテーブルを扱うための短いバージョンは、関数ポインタと使用するポインタを把握する方法を保持するC++ vtableに似たデータ構造を定義することです。ような何か:

typedef void (*dispatch_func)(void *); 
struct dispatch { 
    const char *command; 
    dispatch_func callback; 
}; 

は、引数として単一の匿名のポインタをとる関数に文字列のメソッド名をマップスーパージェネリック版です。

明らか
const struct dispatch commands[] = { 
    { "help", command_help }, 
    { "ihave", command_ihave }, 
    { "quit", command_quit } 
}; 

、より多くのあなたは、関数についてあなたが作ることができ、より具体的に知っている:あなたの実際のディスパッチテーブルは、この(INNソースでtinyleafから取られた変形例)のように、これらの配列となりますプロトタイプを作成し、その他の選択基準(引数の数など)をディスパッチテーブルに埋め込むことができます。 (実際のINNソースは、最小および最大の引数カウントを持ち、void *ではなく引数の構造を渡し、ディスパッチテーブルにコマンドの説明を入れます)

ディスパッチテーブルを検索する基本コードはかなりですシンプル。あなたは、のようなものvtableのものディスパッチ構造体のエントリの配列とlength内のテーブルの長さを持っていると仮定すると:継承を実装するために

for (i = 0; i < length; i++) 
    if (strcmp(command, vtable[i].command) == 0) { 
     (*vtable[i].callback)(data); 
     return; 
    } 

を、あなたは親のポインタを必要とする、とあなたはの終わりから落ちる場合ループ、あなたは親に移動し、ロジックを繰り返します。 (明らかに、これは再帰関数にとっては便利な場所です)。

5

これを行う簡単な方法はありません。それにもかかわらず、C++コンパイラが何をしているのか手動で行うことができます。あなたのケースでは、これは次のようになります。

typedef struct vtable_Fail 
{ 
    int (*val)(struct Fail *this_pointer); 
    bool (*error)(struct Fail *this_pointer); 
} vtable_Fail; 

typedef struct vtable_IntFoo 
{ 
    int (*val)(struct IntFoo *this_pointer); 
    bool (*error)(struct IntFoo *this_pointer); 
} vtable_IntFoo; 

int Fail_val(struct Fail *this_pointer) 
{ 
    return 0; 
} 

... 

void IntFoo_ctor(struct IntFoo *this_pointer, int value) 
{ 
    this_pointer->v = value; 
} 

int IntFoo_val(struct IntFoo *this_pointer) 
{ 
    return this_pointer->v; 
} 

... 

struct Fail 
{ 
    vtable_Fail *vtable; 
}; 

struct IntFoo 
{ 
    vtable_IntFoo *vtable; 
    int v; 
}; 

アイデアは、各構造体は、この構造体が実装するすべての仮想メソッドへのポインタを格納しているコンパニオンのvtable構造体を持つべきであるということです。 Vtableの構造体にはそれぞれ1つのインスタンスしかありません。それらは静的メモリに置かれ、関数へのポインタで挿入されるべきです。これらのポインタは、それぞれの特定の構造体に必要な方法で仮想メソッドを実装する必要があります。構造体自体は多くのインスタンスを持つことができます。インスタンス内のVtableデータは、そのクラスの静的vtable構造体を指す必要があります。

パラメータthis_pointerは、インスタンスのアドレスを渡します。手動で渡す必要があります。これは終日のCです。

メモvtable構造体、init vtableポインタなどを割り当てる必要があることに注意してください。すべてこれを手動で独自のコードで行う必要があります。

関連する問題