Hallo zusammen, ich versuche auf meinem M4-Cortex je nach Menü-Einstellung unterschiedliche Funktionalitäten darzustellen. Bisher habe ich es so realisiert, dass ich je eine eigene Klasse für die Funktionalität erstellt habe. Dort gibt es immer wieder die gleichen Methoden jedoch mit unterschiedlichem Funktionsinhalt. Aufgerufen wurde das Ganze, von einer "Steuer"-Klasse mit einer ewig langen SWITCH/CASE-Wüste. Und das jeweils für die 8 verschiedenen Aufrufe. Nun muss diese SWITCH/CASE-Wüste jedesmal angepasst werden, wenn ich eine neue Funktionalität einbauen möchte. Und ich möchte viele Versionen mit unterschiedlicher Zusammenstellung erstellen. Nun dachte ich mir, es wäre einfacher alles über ein Array-of-functions zu steuern. Einfach statt der SWITCH/CASE einen Aufruf mit dem entsprechenden Index. Nur wie bekomme ich die Klassen bzw. die Methoden in ein Array? Und wie kann ich sie dann aufrufen? Ich hab mir schon eine Klasse mit virtuelle Methoden für die Array-Definition gebaut, komm aber damit nicht weiter. Hab nun schon einige Stunden zusammen mit Tante Google verschiedene Sachen ausprobiert. Nix wollte funktionieren.
:
Verschoben durch Moderator
Forum: Projekte & Code Hier könnt ihr eure Projekte, Schaltungen oder Codeschnipsel vorstellen und diskutieren. Bitte hier keine Fragen posten!
Ja, Projekte und Code ist wohl nicht ganz korrekt. Das kann ein Mod sicher noch verschieben. Eventuell könnte dir das Observer Design Pattern weiterhelfen. http://www.philipphauer.de/study/se/design-pattern/observer.php In diesem Beispiel wäre der Zeitungsverlag dein Menü und die Abonnenten wären deine Funktionsklassen. Gruß Bernhard
Hermi schrieb: > Nur wie bekomme ich die Klassen bzw. die Methoden in ein Array? Und wie > kann ich sie dann aufrufen? Wenn die Funktionen keine Parameter haben, reichen in der Regel function pointer:
1 | #include <iterator> |
2 | #include <iostream> |
3 | |
4 | struct menu_item |
5 | {
|
6 | char key; |
7 | void (*function)(); |
8 | };
|
9 | |
10 | void a_pressed() { std::cout << "A"; } |
11 | void b_pressed() { std::cout << "B"; } |
12 | void c_pressed() { std::cout << "C"; } |
13 | |
14 | static const menu_item menu[] = { |
15 | { 'a', a_pressed }, |
16 | { 'b', b_pressed }, |
17 | { 'c', c_pressed } |
18 | };
|
19 | |
20 | int main() |
21 | {
|
22 | char key; |
23 | |
24 | do
|
25 | {
|
26 | std::cin >> key; |
27 | |
28 | const auto wahl = std::find_if( std::begin( menu ), std::end( menu ), |
29 | [key]( const menu_item& m ) -> bool { |
30 | return key == m.key; |
31 | }
|
32 | );
|
33 | |
34 | if ( wahl != std::end( menu ) ) |
35 | wahl->function(); |
36 | |
37 | } while ( key != 'q' ); |
38 | }
|
Wenn die einzelnen Klassen von einer gemeinsamen Basisklasse abgeleitet sind und die gleichartigen aufzurufenden Methoden virtuell sind, sollte das auch ohne switch und ohne explizite Funktionszeiger gehen.
Tut mir leid, wenn's in falsche Forum gerutscht ist. Welches wäre denn das richtige? @Admin: bitte dahin verschieben. Danke. Bernhard R. schrieb: > Eventuell könnte dir das Observer Design Pattern weiterhelfen. Das denke ich ist etwas übers Ziel hinausgeschossen. Es soll ja auch noch relativ Speichersparend bleiben. Ist ja "nur" ein M4. Und geht das in C++ überhaupt? Torsten R. schrieb: > Wenn die Funktionen keine Parameter haben, reichen in der Regel function > pointer Soweit richtig. Das funktioniert bei mir auch. Sogar mit Parameter, wenn alle die gleichen Parameter haben. Nur hagelt es Fehlermeldungen wenn die Funktionen in den verschiedenen Klassen versteckt sind.
1 | class CommFunct1 { |
2 | public unsigned char init( int parameterNum ){ |
3 | // code
|
4 | }
|
5 | };
|
6 | class CommFunct2 { |
7 | public unsigned char init( int parameterNum ){ |
8 | // code
|
9 | }
|
10 | };
|
11 | class CommFunct3 { |
12 | public unsigned char init( int parameterNum ){ |
13 | // code
|
14 | }
|
15 | };
|
Yalu X. schrieb: > Wenn die einzelnen Klassen von einer gemeinsamen Basisklasse abgeleitet > sind und die gleichartigen aufzurufenden Methoden virtuell sind, sollte > das auch ohne switch und ohne explizite Funktionszeiger gehen. Und wie? Genau das war meine Frage. :-)
Hermi schrieb: > Und wie? Genau das war meine Frage. :-) In dem Du (im obigen Beispiel) die Zeiger auf Funktionen durch Zeiger auf Instanzen der von der Basisklasse abgeleiteten Klassen ersetzt. Alternativ könntest Du auch mit std::function<> alles auf einen Typen bringen, allerdings kann es dann sein, dass std::function<> dynamischen Speicher verwendet, was für Deinen Anwendungsfall eigentlich nicht nötig ist. Warum möchtest Du einen Funktion durch eine Klasse abstrahieren? Haben die "Funktionen" einen Zustand, der zwischen zwei Aufrufen erhalten bleiben muss? Wenn ja, müssen sich dann nicht evtl. sogar mehrere Funktionen den selben Zustand teilen?
Torsten R. schrieb: > Warum möchtest Du einen Funktion durch eine Klasse abstrahieren? Haben > die "Funktionen" einen Zustand, der zwischen zwei Aufrufen erhalten > bleiben muss? Wenn ja, müssen sich dann nicht evtl. sogar mehrere > Funktionen den selben Zustand teilen? Die Klassen haben insgesamt 5 Funktionen. Um die Übersichtlichkeit zu bewahren, haben alle die selben Methodennamen. Die Zustände sollen zwischen den Aufrufen erhalten bleiben und evtl auch zwischen den Funktionen einer Klasse geshared werden. Aber immer nur innerhalb der Klasse. Alle übergeordneten Aktionen werden von der Steuerklasse über die Rückgabewerte und Parameter ausgeführt. Torsten R. schrieb: > Zeiger > auf Instanzen der von der Basisklasse abgeleiteten Klassen ersetzt hast du ein Code-Beispiel dazu wenn die Klassen so aussehen:
1 | class CommFunctEltern { |
2 | public unsigned char init( int parameterNum ){ |
3 | // code
|
4 | }
|
5 | };
|
6 | class CommFunct1 : CommFunctEltern { |
7 | public unsigned char init( int parameterNum ){ |
8 | // code
|
9 | }
|
10 | };
|
11 | class CommFunct2 : CommFunctEltern { |
12 | public unsigned char init( int parameterNum ){ |
13 | // code
|
14 | }
|
15 | };
|
16 | class CommFunct3 : CommFunctEltern { |
17 | public unsigned char init( int parameterNum ){ |
18 | // code
|
19 | }
|
20 | };
|
Hermi schrieb: > hast du ein Code-Beispiel dazu wenn die Klassen so aussehen:
1 | #include <iterator> |
2 | #include <iostream> |
3 | #include <algorithm> |
4 | |
5 | class CommFunctEltern { |
6 | public:
|
7 | virtual unsigned char init( int parameterNum ) = 0; |
8 | protected:
|
9 | ~CommFunctEltern() {} |
10 | };
|
11 | |
12 | class CommFunct1 : public CommFunctEltern { |
13 | private:
|
14 | virtual unsigned char init( int parameterNum ) override { |
15 | std::cout << "CommFunct1" << std::endl; |
16 | |
17 | return 0; |
18 | }
|
19 | };
|
20 | |
21 | class CommFunct2 : public CommFunctEltern { |
22 | private:
|
23 | virtual unsigned char init( int parameterNum ) override { |
24 | std::cout << "CommFunct2" << std::endl; |
25 | |
26 | return 0; |
27 | }
|
28 | };
|
29 | |
30 | class CommFunct3 : public CommFunctEltern { |
31 | private:
|
32 | virtual unsigned char init( int parameterNum ) override { |
33 | std::cout << "CommFunct3" << std::endl; |
34 | |
35 | return 0; |
36 | }
|
37 | };
|
38 | |
39 | static CommFunct1 func1; |
40 | static CommFunct2 func2; |
41 | static CommFunct3 func3; |
42 | |
43 | struct menu_item |
44 | {
|
45 | char key; |
46 | CommFunctEltern* function; |
47 | };
|
48 | |
49 | static const menu_item menu[] = { |
50 | { 'a', &func1 }, |
51 | { 'b', &func2 }, |
52 | { 'c', &func3 } |
53 | };
|
54 | |
55 | int main() |
56 | {
|
57 | char key; |
58 | |
59 | do
|
60 | {
|
61 | std::cin >> key; |
62 | |
63 | const auto wahl = std::find_if( std::begin( menu ), std::end( menu ), |
64 | [key]( const menu_item& m ) -> bool { |
65 | return key == m.key; |
66 | }
|
67 | );
|
68 | |
69 | if ( wahl != std::end( menu ) ) |
70 | wahl->function->init( 1 ); |
71 | |
72 | } while ( key != 'q' ); |
73 | }
|
P.S. in C++ hat man Konstruktoren für die Initialisierung.
:
Bearbeitet durch User
Da bekomme ich folgende Fehlermeldung: Error: Conversion to inaccessible base class "CommFunctEltern" is not allowed in "main.cpp" Torsten R. schrieb: > P.S. in C++ hat man Konstruktoren für die Initialisierung. Mein init hat nix mit der Klasseninitiatilisierung zu tun, sondern mit der funktionsgesteuerten Hardwareinitialisierung.
Hermi schrieb: > Da bekomme ich folgende Fehlermeldung: > Error: Conversion to inaccessible base class "CommFunctEltern" is not > allowed in "main.cpp" In welcher Zeile sollte das sein? Dass könnte nur der Fall sein, wenn Du (wie in Deinem Beispiel) weiterhin private von der Basis-Klasse erbst.
Torsten R. schrieb: > Hermi schrieb: >> Da bekomme ich folgende Fehlermeldung: >> Error: Conversion to inaccessible base class "CommFunctEltern" is not >> allowed in "main.cpp" > > In welcher Zeile sollte das sein? Dass könnte nur der Fall sein, wenn Du > (wie in Deinem Beispiel) weiterhin private von der Basis-Klasse erbst. Es ist in der Zeile, in der die Zuweisung des Klassenobjekts in die struct erfolgt. Ich steh jetzt etwas auf der Leitung. Welche private vererbe ich. Ich hab mal die Echte Klassenbeschreibung hier:
1 | class CommFunct { |
2 | public:
|
3 | CommFunct() { } |
4 | virtual unsigned char init( int parameterNum ) { return( 0 );} |
5 | virtual void loop() { } |
6 | virtual char * getParametername( unsigned int parameterNummer ) { return( NULL );} |
7 | virtual unsigned char getParametertyp( unsigned int parameterNummer ) { return( 0 );} |
8 | virtual unsigned char getParameterAdds( unsigned int parameterNummer, unsigned char * buffer ) { return( 0 );} |
9 | protected:
|
10 | ~CommFunct() {}; |
11 | |
12 | };
|
Und die test-Umgebung
1 | class Testmodul : CommFunct { |
2 | private:
|
3 | virtual unsigned char init( int parameterNum ) override { |
4 | return( 0 ); |
5 | }
|
6 | };
|
7 | |
8 | static Testmodul Tm; |
9 | |
10 | //CommFunct *funcArr[] = { &Tm };
|
11 | |
12 | struct menu_item |
13 | {
|
14 | char key; |
15 | CommFunct* function; |
16 | };
|
17 | |
18 | static const menu_item menu[] = { |
19 | { 'a', &Tm } // hier ist kommt die Fehlermeldung |
20 | };
|
Hermi schrieb: > Und die test-Umgebung >
1 | > class Testmodul : CommFunct { |
2 | >
|
default inheritance in C++ ist für classes private. CommFunct ist also eine private Basisklasse. Damit kann niemand (ausser Testmodul selbst) ein Testmodul da einsetzen, wo ein CommFunct gefordert ist. Du möchtest:
1 | class Testmodul : public CommFunct { |
:
Bearbeitet durch User
Danke! So läufts. Again what learned Ich hab das ganze noch als Array aufgesetzt statt des struct. Damit spar ich mir die Auswahlschleife. Von der Auswahlfunktion kommt sowieso eine fortlaufende Nummer als uint8 zurück.
Hermi schrieb: > Ich hab das ganze noch als Array aufgesetzt statt des struct. Damit spar > ich mir die Auswahlschleife. Von der Auswahlfunktion kommt sowieso eine > fortlaufende Nummer als uint8 zurück. Ich überlege gerade, ob es Sinn macht, wenn deine Funktionsklassen ihren Callback bei der Steuer-Klasse anmelden. Dann müsstest du beim Hinzufügen neuer Funktionen nur die Funktionsklasse schreiben, alles andere wäre sozusagen generisch implementiert. Nur so ein Gedanke, ich hab jetzt auch nicht den ganzen Thread gelesen, weiß auch nicht, ob das mit anderen Konzepten bei dir kollidiert...
Hermi schrieb: > Ich hab mir schon eine Klasse mit virtuelle Methoden für die > Array-Definition gebaut, komm aber damit nicht weiter. Du solltest die Steuer-Klasse mit virtuellen Methoden bauen, dann ist die vtable dein Array, ganz versteckt. Callback nennt man solche Funktionen aber nicht. Eine Callback-Funktion kommt von woanders. Ihre Adresse wird dorthin übergeben. Damit man nicht bloss eine C Funktion sondern eine C++ Memberfunktion aufrufen willst, dann brauch man zusätzlich den self Pointer, den ma neben der callback Funktionsadresse auch übergeben müstte.
Guido schrieb: > Ich überlege gerade, ob es Sinn macht, wenn deine Funktionsklassen > ihren Callback bei der Steuer-Klasse anmelden. Dann müsstest du > beim Hinzufügen neuer Funktionen nur die Funktionsklasse schreiben, > alles andere wäre sozusagen generisch implementiert. Nur so ein Gedanke, > ich hab jetzt auch nicht den ganzen Thread gelesen, weiß auch nicht, ob > das mit > anderen Konzepten bei dir kollidiert... Da das ganze auf einem Arm-M4-Cortex-Controller läuft und alles bei Compilezeit definiert ist, wäre das Anmelden der Funktion nur zusätzlich Code-Speicher-Ausführungszeit. Leider finde ich die Platzierung des Beitrags im PC-Programmierungsforum etwas unglücklich. Aber soll wohl so sein. MaWin schrieb: > Callback nennt man solche Funktionen aber nicht. Gut, aber wie heißts dann? MaWin schrieb: > Hermi schrieb: >> Ich hab mir schon eine Klasse mit virtuelle Methoden für die >> Array-Definition gebaut, komm aber damit nicht weiter. > > Du solltest die Steuer-Klasse mit virtuellen Methoden bauen, dann ist > die vtable dein Array, ganz versteckt. Ich denk, mein Weg war nicht ganz verkehrt. Nur leider mangels Wissen/Erfahrung nicht ganz fertig. Dank Torsten Robitzki läufts jetzt und ist auch relativ schlank. Ich habe meine Funktionen in eigenen abgeschlossenen Klassen/Files, Die Steuerklasse ist auch schön separiert und ich kann alles, nach Bedarf, in meinem main.cpp zusammennageln.
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.