Vererbung in C? (allgemein, nicht nur GCC)

#1449942
Lesenswert?

Hallo,

gibt es einen offiziellen Weg folgendes in C zu realisieren?

Ich habe mehrere Hardwarekomponenten (UART, USB, ...).

Alle können Datenströme senden und empfangen. D.h. für jede 
Hardwarekomponente gibt es ein struct, welches die Komponente verwaltet.
Nun haben diese structs gemeinsamme Elemente. Z.B. verfügt jede 
Hardwarekomponente über einen Send- und Empfangspuffer + dazugehörige 
Pointer. Diese sind für alle Hardwarekomponentet/structs gleich.

Nun möchte ich eine C-Funktion schreiben, der ich jede dieser 
verschiedenen structs übergeben kann.
Wichtig wäre da aber, daß z.B. der RxBuffer in allenen structs den 
gleichen Offset hat.

In C++ würde man da ja z.B. eine struct/class entwerfen, die dieses 
gemeinsammen Elemente enthält. Alle speziellen structs/classes würden 
dann von dieser erben.
Somit könnte man der Funktion einen Pointer vom Typ Basisklasse 
übergeben und man kann sicher sein, daß die in der Basisklasse 
deklarieretn Elemente für alle Hardwarekomponenten an der gleichen 
stelle liegt.

Wie macht man das in C?
Persönliche Seite #1450072
Lesenswert?

Die einzelnen IO-Typen (oder Zeiger darauf) in eine Union stopfen?
1
#include "usb.h"
2
#include "uart.h"
3
#include "i2c.h"
4
...
5

6
enum
7
{
8
    IO_USB,
9
    IO_UART,
10
    IO_I2C,
11
    ...
12

13
    IO_NIL
14
};
15

16
typedef union
17
{
18
    int id;
19

20
    struct
21
    {
22
        int id;
23
        usb_t data;
24
    } usb;
25

26
    struct
27
    {
28
        int id;
29
        uart_t data;
30
    } uart;
31

32
    ...
33

34
} io_t;
35

36
void handle_io (io_t * io)
37
{
38
    switch (io->id)
39
    {
40
        case IO_USB:
41
            io->usb.data.buffer[0] = '0';
42
            break;
43

44
        case IO_UART:
45
            io->uart.data.buffer[0] = '0';
46
            break;
47

48
        ...
49

50
        default:
51
            fatal_error();
52
            break;
53
    }
54
}

Das ist keine Verebung, aber es gibt dir verschiedene Sichten auf dein 
io-Objekt. Und in uart.h:
1
#include "buffer.h" // extern char buf[];
2

3
typedef struct
4
{
5
    ...
6
    char * buffer;
7
    ...
8
} uart_t;
9

10
void uart_init (uart_t * uart)
11
{
12
    uart->buffer = buf;
13
    ...
14
}
Gast #1450078
Lesenswert?

>Was meinst Du mit "als CPP define"?
einfach eine preprozessor define, habe dir ein Beispiel dort gemacht.
struct hw_serial { HW_CLASS; char dummy[100]; } hw_serial1,hw_serial2;

>Und was meinst Du mit "sauber, aber nicht portabel"? Solange es die
>Compiler unterstützen und ich den Datenblock nicht blind auf ein anderes
>System übertrage, sollte es doch gehen, oder?

Ja, wenn es deine Compiler unterstützen.

Z.B.

struct hw_base { int size; char *head; char*tail; ... };

struct hw_rs232 { struct hw_base; int port; ... } serial1, serial2;

dann kannst du im Code z.B.  serial1.size usw benutzen, als hättest du
sie in hw_rs232 declariert.

Bei neueren Branches des GCC kann es sein, daß du -fms-extensions als
Kommandozeile übergeben mußt, damit er das macht, früher konnter es es 
auch
ohne.
Persönliche Seite #1450080
Lesenswert?

Johann L. schrieb:

> Das ist keine Vererbung, aber es gibt dir verschiedene Sichten auf dein
> io-Objekt. Und in uart.h:

.uart.data.buffer und .usb.data.buffer liegen nicht notwendig an der 
gleichen Adresse (was auch nicht nötig ist) aber sie zeigen auf die 
gleiche Adresse, wenn diese Zeiger richtig initialisiert wurden (siehe 
unten). Beachte, daß das dann nicht reentrant-fähig ist.
Gast #1450173
Lesenswert?

> Habe ich mir schon gedacht. Ich hatte aber gehofft, daß ich die doppelte
> Indirektion umgehen kann.

Ein CPP-Compiler macht bei Vererbung nix anderes.
Aus
1
class Dings : public class Bums {
2
   int some_field;
3
};
4

5
...
6
Dings d;
7
Bums *b = &d;

wird
1
struct Dings {
2
   struct Bums _base;
3
   int some_field;
4
};
5

6
...
7
struct Dings d;
8
struct Bums *b = &(d._base);

Da bei Einfachvererbung das Basisobjekt an den Anfang des Gesamt-Structs 
gepackt wird, ist diese Indirektion übrigens im Maschinencode recht 
trivial ;-)

Und, wie schon von den Vorpostern genannt: Nimm nen C++-Compiler. Du 
musst ja außerhalb dieser Stelle keine C++-Features nutzen, von daher 
hat dies auf die Codegröße keine negativen Auswirkungen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren