Hallo,
ich möchte eine Bibliothek möglichst variabel gestalten und komme dabei
an meine Grenzen in der Programmierung.
Speziell möchte ich verschiedene Displaytreiber an eine übergeordnete
Grafik-Bibliothek übergeben, da ich 3 Grafik-Displays (2 verschiedene
Typen) an einen Controller anschließen muss.
Der Treiber für die Displays ist so Aufgebaut, dass immer ein Struct mit
allen Informationen übergeben wird.
Jetzt möchte ich zum Beispiel mit meiner Grafikbibliothek etwas
zeichnen, Dazu muss ich den Funktionspointer und das Struct mit den
Schnittstellen Infos übergeben.
1
voidgrafiklib(fctptr,struct)
2
{
3
// mach etwas
4
fctptr(struct,123,240);
5
}
Mein Problem ist, dass ich verschiedene Displaytreiber mit der Grafiklib
verwenden möchte (ili9341, S65, Nokia, ...). Die Grafikfunktionen der
Treiber sind immer gleich. Wie kann ich das in C realisieren, denn ich
kann ja der Funktion "void grafiklib(fctptr, struct)" nicht verschiedene
typen (structs) übergeben.
... oder wie kann ich das anders aufbauen.
Ich hoffe mein Problem ist so einigermaßen klar geworden.
Martin J. schrieb:> Ich hoffe mein Problem ist so einigermaßen klar geworden.
Sorry, mir leider nicht.
Das fctptr im obigen Beispiel ist was? Eine Funktion von einem fremden
Treiber, den du nur übernimmst?
1) Origanisiere die Daten für die verschiedenen Treiber so, dass sie
alle das gleiche Layout haben. Evtl. ist dann Info in den Daten die ein
Treiber nicht braucht.
oder
2) Lege die Strukturen in eine Union. Jede Struktur und die Union haben
als erstes Element ein Datum, dass die Daten identifiziert:
1
typedefunion
2
{
3
intid;
4
5
struct
6
{
7
intid;
8
...
9
}foo;
10
struct
11
{
12
intid;
13
...
14
}bar;
15
...
16
}data_t;
17
18
voidf(data_t*data)
19
{
20
if(data->id==DATA_FOO)
21
{
22
// use data->foo
23
}
24
}
oder
3) Nimm einen void* und caste zu dem Datenzeiger der gebraucht wird
(nicht so toll).
Ich glaube, mir dämmert dein Problem.
Die struct soll sich von Treiber zu Treiber irgendwie unterscheiden,
trotzdem willst du sie über dieselbe Parameterliste bringen, und
letztlich landet sie pber den Funktionspointer als Parameter bei der
richtigen Funktion?
Dann wäre evtl. eine union der richtige Weg:
Du schreibst für jeden Treiber die struct, machst von allen eine union
und übergibst jeweils den Funktionszeiger und einen Zeiger auf die
union.
Die Funktion nimmt sich dann aus der union das, was sie braucht.
Das ist der Anwendungsfall für C++ und virtuelle Funktionen.
Einfacher, kürzer, fehlersicherer, und effizienter als die C-Wurstelei
mit Funktionspointern.
1
classDisplayDriver{
2
public:
3
virtualvoidsetPixel(intx,inty,intcolor)=0;
4
/* hier weitere Funktionen die jedes Display unterstützen muss, zB Bereiche ausmalen, ein/aus schalten etc. */
5
};
6
7
classDriverili9341:publicDisplayDriver{
8
public:
9
virtualvoidsetPixel(intx,inty,intcolor){
10
/* .. display-spezifisches Pixelsetzen */
11
}
12
/* Hier o.g. weitere Funktionen unterbringen */
13
14
/* Hier displayspezifische Variablen unterbringen, die sonst in das struct kommen würden */
15
uint8_tpin_cs;
16
uint8_tpin_rs;
17
uint8_tpin_dc;
18
PORT_t*port;
19
SPI_Master_t*spi;
20
};
21
22
// Funktion der GrafikLib die mit beliebigen Display-Typen funktioniert.
Klaus Wachtler schrieb:> ja, aber Masochismus ist verbreitet, das muß man akzeoptieren :-)
Ich lasse mir zwar auch gerne den Hintern versohlen, aber so schlimm
dass ich so etwas in C machen würde ist es noch nicht!
structdriver_entry*my_driver=&drivers[1];/* nehmen wir mal den */
4
5
my_driver->init();
6
my_driver->draw_line(0,0,10,10);
7
my_driver->exit();
8
}
Sooo viel schöner ist das in C++ auch nicht. Und wenn die
unterschiedlichen "Treiber" unterschiedliche Funktionen brauchen, muß
man halt noch eine union dazwischen hängen.
Markus F. schrieb:> Und wenn die> unterschiedlichen "Treiber" unterschiedliche Funktionen brauchen, muß> man halt noch eine union dazwischen hängen.
Und wenn ein Treiber viel mehr Speicher braucht als die anderen, muss
man für jeden anderen genauso viel Speicher verschwenden, da ein union
immer mindestens so groß ist wie das größte Element...
Markus F. schrieb:> Sooo viel schöner ist das in C++ auch nicht.
Immerhin muss man die vtable (dein drivers array) nicht von Hand
anlegen. Wenn man mehrere ähnliche Treiber hat, kann man eine abstrakte
Zwischen-Klasse anlegen, und dann spart man erst Recht Tipparbeit, und
geht einer größeren Fehlerquelle aus dem Weg... Warum auf den Komfort
verzichten? Warum wollen immer alle um jeden Preis C++ vermeiden, auch
wenn es perfekt geeignet ist?
Programmierer schrieb:> Warum wollen immer alle um jeden Preis C++ vermeiden, auch> wenn es perfekt geeignet ist?Ich hätte das (möglicherweise) tatsächlich mit C++ gemacht.
Möglicherweise sogar obwohl Libraries mit C++ manchmal überhaupt
keinen Spaß machen...
Der Fragesteller hat aber offensichtlich mit C schon seine Probleme,
warum ihm also 'nen Daimler aufschwatzen, wenn er eigentlich ein Fahrrad
wollte? ;)
Markus F. schrieb:> Ich hätte das (möglicherweise) tatsächlich mit C++ gemacht.> Möglicherweise sogar obwohl Libraries mit C++ manchmal überhaupt> keinen Spaß machen...
Man kann in jeder Sprache Murks produzieren...
> Der Fragesteller hat aber offensichtlich mit C schon seine Probleme,> warum ihm also 'nen Daimler aufschwatzen, wenn er eigentlich ein Fahrrad> wollte? ;)
Vielleicht weil er damit schneller und bequemer über die Alpen kommt...
Klaus Wachtler schrieb:
> Ich glaube, mir dämmert dein Problem.
Genau :-)
Allen schon mal vielen Dank für die Ideen. Ja man könnte das auch schön
elegant in C++ realisieren, doch dann müsste ich meine ganzen anderen
Bibliotheken ändern und darauf habe ich grad keine Lust. ;-)
Am Ende ist die Verwendung der Structs für die Variable
SPI-Schnittstelle der Treiber ja auch nur ein Pseudo C++, so dass man
weiter C verwenden kann.
Ich hatte nur gedacht, dass es für solche Fälle noch andere
Möglichkeiten in C gibt. Immerhin wurden auch sehr Komplexe Programe in
C geschrieben, die eine ähnliche Flexibilität erfordern.
Danke.
Martin J. schrieb:> doch dann müsste ich meine ganzen anderen> Bibliotheken ändern und darauf habe ich grad keine Lust. ;-)
Was musst du denn da alles ändern? Hast du 1000x die Bezeichner "this"
und "class" verwendet? Die paar fehlenden void* -> T* casts findet der
Compiler und sie sind schnell behoben. Außerdem spricht auch nichts
dagegen, den Rest als C zu kompilieren...
Funktionszeiger gibt es seit Anno Tobak.
Wenn Du sowieso eine Struktur initialisierst, spendiere ihr doch dabei
gleich die passende Funktion.
Leider ist das "Steinzeit-C".
Virtuelle Funktionen gibt es zwar nicht ganz so lange (C++), aber
manchmal hilft es eine Pupille ins nächst verfügbare C-Buch zu
schmeißen.
Ist ganz einfach; beiß nicht.
Es gibt wohl kaum Lehrbücher, in denen gerade am Beispiel von grafischen
Ausgaben, diese Mechanismen nicht erläutert werden. Da wird zwar oft
eine virtuelle Funktion mit Namen print verwendet, mit der dann ein
Etwas (Punkt, Kreis, Linie usw.) ausgegeben wird, das Prinzip ist aber
übertragbar.