2013-03-31 43 views
7

Mam nadzieję znaleźć podpowiedź (najlepiej poprzez dobry przykład) za realizację dynamicznego wysyłkę w C.Dynamiczny Wysyłamy w C stosując metodę wirtualną tabelę

uczę się C i jako praktyk, chcę tłumaczyć z Java C przy użyciu wirtualnej tabeli metod dynamicznej wysyłki.

na przykład mam kodu 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;} 
} 

a może po prostu tłumaczyć pewne podstawowe rzeczy jak poniżej:

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

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

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

siedzę, starając się wypełnić to, bo nie know:

  1. Jak zdefiniować te metody w c.?
  2. Ustaw adres tych metod w vtable, aby kompilator rozpoznał właściwą metodę wywoływania.
  3. Co jeszcze należy zdefiniować, aby działało.
+0

Zamiast próbować zgniatać okrągły kołek w kwadratową dziurę - aby spróbować wymusić strukturę OOP w języku innym niż OOP, używałbym C++, a nie C do tego. –

+3

@HovercraftFullOfEels Szukam, aby zrobić to wc tylko dlatego, że jestem poproszony przez mojego profesora. – Solix

+1

* Źródłem * tego rodzaju rzeczy jest [ooc.pdf] (http://www.cs.rit.edu/~ats/books/ooc.pdf) autorstwa [Axel Tobias Schreiner] (http: // www. .cs.rit.edu/~ ats/books /). –

Odpowiedz

3

Podejście, które polecam, to spojrzenie na inny kod C, który wykorzystuje tabelę wysyłkową i zobacz, jak jest zorganizowany. Konkretne przykłady, o których jestem świadomy z góry mojej głowy (być może nie są to najlepsze przykłady nauczania, ponieważ są po prostu przypadkowym wolnym oprogramowaniem, nad którym pracowałem) to MIT Kerberos i Heimdal, obie używają tablic ekspedycyjnych na całym miejsce i serwer WWW Apache oraz sposób, w jaki obsługuje dynamicznie ładowane moduły. Żadne z nich nie dziedziczy, ale dziedziczenie jest stosunkowo łatwe do dodania: widzisz, czy metoda, którą próbujesz wywołać, ma wartość NULL, a jeśli tak, to sprawdzasz rodzicielską tabelę wysyłkową dla tej metody.

Krótka wersja dla obsługi tablicy wysyłkowej w C ogólnie jest taka, że ​​definiujesz strukturę danych podobną do tabeli v ++ C++, która zawiera wskaźniki funkcji i pewien sposób ustalania, który wskaźnik użyć. Coś jak:

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

jest wersja super-generic, który mapuje nazwy metod ciąg do funkcji, które mają jeden anonimowy wskaźnik jako argument. Rzeczywisty stół wysyłka będzie tablicą z nich, jak to (zmodyfikowany przykład wzięty z tinyleaf w źródle INN):

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

Oczywiście, im więcej wiesz o funkcjach, tym bardziej specyficzny można zrobić prototyp i możesz wstawić inne kryteria wyboru (takie jak liczba argumentów) do tabeli wysyłkowej. (Faktyczne źródło INN ma minimalną i maksymalną liczbę argumentów, przechodzi w strukturę argumentów zamiast tylko pustki * i zawiera opis komendy w tabeli wysyłki.)

Podstawowy kod do wyszukiwania w ekspedycji jest ładny prosty. Zakładając, że szereg tych wpisów wysyłka struct w vtable a długość stołu w length, coś jak:

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

Aby zaimplementować dziedziczenie, musisz wskaźnik nadrzędną, a jeśli spadnie na koniec w pętli przejdziesz do rodzica i powtórzysz logikę. (Oczywiście, może to być przydatne miejsce dla funkcji rekursywnej).

5

Nie ma prostego sposobu robienia tego. Niemniej jednak można ręcznie zrobić to, co robi kompilator C++.W twoim przypadku będzie to wyglądać tak:

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; 
}; 

Chodzi o to, że każdy powinien mieć struct towarzysz vtable struct, który przechowuje wskaźniki do wszystkich metod wirtualnych tej struktury narzędzi. Struktury Vtable mają tylko jedną instancję. Powinny one znajdować się w pamięci statycznej i powinny być zainicjowane wskaźnikami do funkcji. Wskaźniki te powinny implementować metody wirtualne w sposób wymagany dla każdej konkretnej struktury. Same struktury mogą mieć wiele instancji. Vtable pola danych w instancji powinny wskazywać na static vtable struct jej klasy.

Parametr this_pointer przekazuje adres instancji. Powinien być przekazywany ręcznie. To jest C na koniec dnia.

Uwaga należy przydzielić struktury vtable, wskaźniki init vtable itp. I trzeba to zrobić ręcznie za pomocą własnego kodu.