Hallo, weis nicht ob das hier oder im PC Forum besser aufgehoben ist. Aber eigentlich eher eine Frage zu C, also => gcc. Ich schreibe gerade ein Modul dass eine bestimmte Datenstruktur und verschiedene Operationen auf diese anbietet. Organisiert sind die Daten in einer Struktur. Durch eine Init Funktion sollen mehrere "Instanzen" dieser Struktur angelegt werden können. Die Schnittstellen des Moduls werden durch das Einbinden der Header Datei dem Programm bekannt gemacht. Der "Benutzer" soll beim Aufrufen einer Funktion immer die Instanz der Datenstruktur übergeben auf die operiert wird. Am liebsten wär mir das über ein Handle. Wenn der Benutzer nämlich anstelle eines Handles die struct an sich sieht (und einen Zeiger auf diese übergibt) könnte er ohne Umweg über die Schnittstellen direkt an den Membern der struct rummanipulieren. Aber bei einem Zugriff per Handle muss ich ja intern in meinem Modul irgendwie eine Liste führen, in der die Strukturen abgelegt sind. Da setzts grad aus bei mir. Soll ich mein struct um zwei Member erweitern um eine doppelt verkettete Liste zu verwalten? Aber das geht ja wieder mit Overhead für die Listenverwaltung einher. Denkbar wäre noch ein Array mit Zeigern auf die structs. Aber ich weiß nicht wie leicht oder schwer es ist, ein Array dynamisch zu vergrößern oder zu verkleinern. Wärend ich das schreibe merke ich übrigens, dass mein Vorhaben ein Paradebeispiel für OOP wäre... Wisst ihr eine Lösung, das möglichst elegant hinzukriegen? Zur Not muss ich die struct eben doch nach aussen hin bekannt geben, aber das halte ich wie gesagt für unschön.
Da hast du zwei Möglichkeiten (mit Untervarianten natürlich): 1. Du vergibst als Handles irgendwelche IDs, z.B. fortlaufende Nummern, und merkst dir in einer Tabelle, welche Id zu welcher struct (bzw. Zeiger darauf) gehört. 2. Du redest dem Anwender gegenüber von einem Handle, zeigst ihm aber nur eine unsigned int passender Größe (so daß ein Zeiger reinpasst) oder wahlweise void*, und nur du selber weisst, daß sich dahinter tatsächlich die Adresse der zugehörigen struct verbirgt. Der Anwender gibt dir also seine uint32_t/uint64_t oder void*, und du castest das dann jeweils in einen Zeiger auf die struct. 1. ist rechenzeitintensiver, weil anhand bsearch() oder einer Hashtabelle o.ä. jeweils von der ID auf die Adresse übersetzt werden muß. Am schnellsten ist vermutlich, wenn deine Handles bei 0 starten und tatsächlich ein Eintrag in ein Feld sind; darin finden sich dann die Adressen der tatsächlichen structs. Dann geht die Übersetzung durch einen Feldzugriff und damit für die meisten Fälle wohl schnell genug. Falls das Feld mit malloc() beschafft wird, kann es mit realloc() verkleinert oder vergrößert werden. 2. ist insofern etwas unelegant, als man bewusst casten muß. Bei void* ahnt der Anwender natürlich, daß sich die Adresse dahinter verbirgt, auch wenn sie ihn nichts angeht, und bei einer ganzzahligen Zahl musst du dafür sorgen, daß auf jedem verwendeten System ein Handle groß genug ist für eine Adresse, und das Wandeln von Adresse zu Zahl und zurück gilt nicht als elegant per se. Dafür ist diese Methode am schnellsten, weil faktisch nichts gerechnet werden muß, um vom Handle zur struct zu kommen.
Wenn du magst kannst du das komplett so kapseln, daß Zugriffe auf die Daten in main.c zu einem Syntaxfehler führen:
1 | // data.h
|
2 | struct data_s; |
3 | |
4 | typedef struct data_s *data_t; |
5 | |
6 | extern data_t data_create (void); |
7 | extern void data_init (data_t); |
8 | |
9 | // main.c
|
10 | |
11 | void func (void) |
12 | {
|
13 | data_t data; |
14 | |
15 | data = data_create (); |
16 | data_init (data); |
17 | }
|
18 | |
19 | // data.c
|
20 | |
21 | struct data_s |
22 | {
|
23 | int a, b; |
24 | };
|
25 | |
26 | #include <stdlib.h> |
27 | |
28 | data_t data_create (void) |
29 | {
|
30 | return (data_t) malloc (sizeof (struct data_s)); |
31 | }
|
32 | |
33 | void data_init (data_t d) |
34 | {
|
35 | d->a = 0; |
36 | d->b = 0; |
37 | }
|
data.h stellt die Interfaces für main.c zu Verfügung. data_t is ein Zeiger auf einen incomplete Type der erst in data.c zu einem vollständigen Typen gemacht wird. Erst dadurch kann in data.c via data_t auf die Komponenten von data_s zugegriffen werden.
Wie wär' es mit dieser Idee: Im Header-File nur ein struct abc; und dann alle Funktionen Pointer auf diesen Typ akzeptieren bzw. zurückgeben lassen. So kann kein User auf die Elemente der Struktur zugreifen, aber trotzdem sind keine dummen Casts nötig. EDIT: Da war wohl jemand schneller und ausführlicher als ich.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.