Ich glaube ich brauche mal etwas nachhilfe in C++. :-) Es geht um diese Umgebung hier, aber ich denke das ist nicht so wichtig: https://circle-rpi.readthedocs.io/en/44.3/ Ich will mir eine Klasse schreiben mit der ich ueber SPI ein Display ansteuern kann. Das ist auch kein Problem, bekomme ich hin. Dort werde ich je nach bedarf Pixel in einem Framebuffer setzen. Dann will eine Methode haben die 100x pro Sekunde aufgerufen wird und eventuelle Aenderungen des Framebuffers an das Display rausschreibt. Es gibt bereits eine Funktion die meine Methode aufrufen kann: void CTimer::RegisterPeriodicHandler(TPeriodicTimerHandler *pHandler) Der kann ich eine Methode uebergeben welche dann vom System aufgerufen wird. Soweit so gut. Leider muss diese Methode aber static definiert sein. Damit hab ich in der Methode aber z.B keinen Zugriff mehr auf meine SPI Klasse. Wie loest man sowas normalerweise? Olaf
Du brauchst von der Klasse hoffentlich nur ein Objekt? Dann muß die static-Methode dieses Objekt nehmen und darüber eine Methode aufrufen, die dann auf die Interna des Objekts zugreifen kann. Evtl. muß die static-Methode vor dem Zugriff testen, ob das Objekt initialisiert ist und noch nicht wieder aufgelöst. Wenn du mehrere Objekte brauchst, müsste man sich ein Handle oder sowas für jedes Objekt definieren (im einfachsten Fall ein Zeiger auf das Objekt), um die Objekte zu unterscheiden. PS: evtl. wäre es ja aber auch eine Lösung, eine entsprechende Logik in deine Klasse einzubauen, also etwas wie CTimer::RegisterPeriodicHandler() in deiner Klasse. Dann hätte sich das Problem erledigt.
:
Bearbeitet durch User
> Dann muß die static-Methode dieses Objekt nehmen und darüber eine > Methode aufrufen, die dann auf die Interna des Objekts zugreifen kann. Das verstehe ich nicht. Ich kann ja aus der static methode nicht auf die anderen Methoden diese Klasse zugreifen. Das ist ja gerade mein Problem. Olaf
Von deinem Code heraus gibt es keine saubere Möglichkeit das zu lösen. Der Autor von Circle hat da gepennt, und halt nur einen Funktionszeiger vorgesehen, obwohl es eine C++-Library ist. Eine einfache und klassische Möglichkeit das zu lösen wäre es, den Funktionszeiger durch eine abstrakte Klasse mit Callback zu ersetzen. Im Anhang ein kleiner Patch, mit dem man das in Circle nachrüsten könnte. Du kannst damit dann deine Display-Klasse von CPeriodicTimerHandler ableiten, eine Member-Funktion
1 | virtual void onTimer () override; |
hinzufügen, und den Timer einfach mit
1 | timer.RegisterPeriodicHandler(this); |
registrieren. Deine onTimer-Funktion wird dann als Callback verwendet.
Olaf schrieb: > Das verstehe ich nicht. Ich kann ja aus der static methode nicht auf die > anderen Methoden diese Klasse zugreifen. Das ist ja gerade mein Problem. So etwa hatte ich das gemeint:
1 | #include <iostream> |
2 | |
3 | |
4 | // Typ der callback-Funktion:
|
5 | typedef void(TPeriodicTimerHandler)(void); |
6 | |
7 | namespace CTimer |
8 | {
|
9 | void RegisterPeriodicHandler(TPeriodicTimerHandler *pHandler) |
10 | {
|
11 | std::cout << "registriere irgendwas..., pHandler=" << (void*)pHandler << std::endl; |
12 | // hier könnte ein Thread gestartet werden, der regelmäßig
|
13 | // pHandler() aufruft...
|
14 | |
15 | // Testweise rufe ich die callback einmal auf:
|
16 | (*pHandler)(); |
17 | }
|
18 | }
|
19 | |
20 | |
21 | class MeineKlasse; |
22 | MeineKlasse *p_globalesObjekt = nullptr; |
23 | |
24 | |
25 | class MeineKlasse |
26 | {
|
27 | public:
|
28 | MeineKlasse( int wert ) |
29 | : wert( wert ) |
30 | , initialisiert( false ) |
31 | {
|
32 | // alles intialisieren, was nötig ist...
|
33 | |
34 | // zum Schluß markieren, daß ich ein vollwertiges Mitglied der
|
35 | // Gesellschaft bin:
|
36 | initialisiert = true; |
37 | }
|
38 | |
39 | ~MeineKlasse() |
40 | {
|
41 | initialisiert = false; |
42 | }
|
43 | |
44 | // Methode, die internes Zeug braucht:
|
45 | void tuwas() |
46 | {
|
47 | std::cout << "tuwas(): wert ist " << wert << std::endl; |
48 | }
|
49 | |
50 | // statische Methode, die als callback funktioniert, aber nicht
|
51 | // internes kennt (weil static):
|
52 | // Muß TPeriodicTimerHandler entsprechen.
|
53 | static void staticTuwas() |
54 | {
|
55 | // Weil tuwas() nur mit einem Objekt aufgerufen werden kann, muß
|
56 | // die static-Methode dieses globale Objekt kennen.
|
57 | |
58 | if( p_globalesObjekt && p_globalesObjekt->istInitialisiert() ) |
59 | {
|
60 | p_globalesObjekt->tuwas(); |
61 | }
|
62 | else
|
63 | {
|
64 | std::cerr << "staticTuwas(): kein globales Objekt..." << std::endl; |
65 | }
|
66 | }
|
67 | |
68 | bool istInitialisiert() |
69 | {
|
70 | return initialisiert; |
71 | }
|
72 | |
73 | private:
|
74 | |
75 | int wert; |
76 | |
77 | bool initialisiert; |
78 | };
|
79 | |
80 | |
81 | int main( int nargs, char **args ) |
82 | {
|
83 | // Objekt erzeugen:
|
84 | p_globalesObjekt = new MeineKlasse( 42 ); |
85 | |
86 | // call back registrieren:
|
87 | CTimer::RegisterPeriodicHandler( &MeineKlasse::staticTuwas ); |
88 | |
89 | // irgendwas fleißig arbeiten...
|
90 | |
91 | // Aufräumen:
|
92 | delete p_globalesObjekt; p_globalesObjekt = nullptr; |
93 | }
|
MeineKlasse::staticTuwas() ist eine statische Methode, die man an CTimer::RegisterPeriodicHandler() übergeben kann. Die kennt nicht die Interna eines Objekts. Sie kann aber über den Zeiger auf ein Objekt zugreifen, und darüber dann tuwas() aufrufen - das macht wiederum die eigentliche Arbeit. Das Gedöns mit initialisiert braucht man nur, wenn wegen Multithreading oder Interrupts der Ablauf unklar wird. Weiß man, daß die callback nur aufgerufen wird, solange das Objekt vollständig ist, kann man sich das natürlich sparen. Das ist bei mir kompilierbar und tut, was ich erwarte:
1 | registriere irgendwas..., pHandler=0x55c7495053ee |
2 | tuwas(): wert ist 42 |
:
Bearbeitet durch User
Wenn "MeineKlasse" aber auf diese globale Variable zugreift, kann man sich auch gleich sparen hier eine Klasse umzusetzen, man kann dann sowieso nicht mehrere Instanzen davon anlegen. Wenn schon kann man eine freie Hilfsfunktion anlegen welche den Aufruf an die globale Instanz weiterleitet, sodass wenigstens die Klasse selbst von diesem Hack unberührt bleibt.
Weniger gruselig-javaesk als der TPeriodicTimerHandler aus dem diff und viel flexibler wäre std::function, wenn man nicht auf antike C++-Standards angewiesen ist. Wenn CTimer unverändert bleiben muss, wird es hässlich. Wenn die eine statische Methode nicht ausreicht (bei mehreren Instanzen) und die Gesamtanzahl der Callbacks überschaubar und begrenzt ist, wäre die Variante im Anhang mit einer Adapter-Klasse, die einen Vorrat an statischen Methoden hat, eine Möglichkeit.
Tom schrieb: > Weniger gruselig-javaesk als der TPeriodicTimerHandler aus dem diff und > viel flexibler wäre std::function, wenn man nicht auf antike > C++-Standards angewiesen ist. Naja, Java hat das von C++ übernommen, also ist das eher Oldschool-C++-esk. std::function hat den Nachteil, dynamischen Speicher zu brauchen. Es gibt auch Embedded-kompatible Varianten mit begrenztem fixem Speicher. Das benutzt intern allerdings auch virtuelle Funktionen ;-)
Programmierer schrieb: > Wenn "MeineKlasse" aber auf diese globale Variable zugreift, kann man > sich auch gleich sparen hier eine Klasse umzusetzen, man kann dann > sowieso nicht mehrere Instanzen davon anlegen. War die Vorgabe des TO: Klasse und eine statische Methode, die registriert werden kann. Ich sagte oben schon: wenn man mehrere Objekte braucht, müsste man sie mit Handles o.ä. auseinanderhalten. Aber er will ein Display ansteuern damit. EIN Display. Programmierer schrieb: > Wenn schon kann man eine > freie Hilfsfunktion anlegen welche den Aufruf an die globale Instanz > weiterleitet, sodass wenigstens die Klasse selbst von diesem Hack > unberührt bleibt. Kann man, ja. Ob man eine statische Methode macht oder stattdessen eine Funktion außerhalb, ist technisch dasselbe. Er wollte eine statische Methode haben...
Okay, anstatt hier zu lesen, hab ich die letzte Stunde mein Gehirn angestrengt und selber was gemacht. Allerdings qualmt jetzt mein Gehirn. :-) Ich stell es mal zur diskussion: st7735.h
1 | void Ticker(void); |
2 | static void WrapTicker(void); |
st7735.cpp
1 | void *ST7735Pointer; |
2 | |
3 | void ST7735::Initialize(void) |
4 | {
|
5 | my_Timer->RegisterPeriodicHandler(WrapTicker); |
6 | }
|
7 | |
8 | void ST7735::WrapTicker(void) |
9 | {
|
10 | ST7735 *ich = (ST7735*) ST7735Pointer; |
11 | ich->Ticker(); |
12 | }
|
13 | |
14 | void ST7735::Ticker(void) |
15 | {
|
16 | static u8 TxData[10]; |
17 | u8 RxBuffer[10]; |
18 | |
19 | TxData[0] = TxData[0] +1; |
20 | |
21 | //Übertragen eines Bytes
|
22 | my_SPIMaster->WriteRead (SPI_CHIP_SELECT, TxData, RxBuffer, 1); |
23 | |
24 | }
|
kernel.cpp
1 | extern void *ST7735Pointer; |
2 | lowlcd = new ST7735(&m_Logger, &m_Timer); ST7735Pointer = (void*) lowlcd; |
Fühlt sich ein bisschen so an als würde man die Idee von c++ ermorden, aber funktioniert. :-D Die Idee den Source von circle zu ueberarbeiten war mir auch schon gekommen, ist aber bloed wenn man dann irgendwann auf eine neuere Version updatet und alles wieder kaputt ist. Olaf
Olaf schrieb: > Fühlt sich ein bisschen so an als würde man die Idee von c++ ermorden, > aber funktioniert. :-D Na ja, das ist halt der Preis, den man für ein Legacy C-Style-Interface beim Timer zahlt. Wäre das ein anständiges templatebasiertes C++-Callback-Interface, wärs halt mehr C++. Oliver
Mein Vorschlag (kombiniert mehrere obige Vorschläge + P-IMPL): /////// // Timer.h class ITimer { public: virtual ~ITimer() {} virtual void onUpdate() = 0; }; class Timer : public ITimer { class Timer_Private* m_private; public: Timer(); ~Timer(); void onUpdate() override; } //////// // Timer.cpp // static adapter class static class Timer_Private { ITimer* m_timer; public: Timer_Private( ITimer & timer ) : m_timer( &timer ) { CTimer::RegisterPeriodicHandler( &Timer_Private::onUpdate ); } static void onUpdate() { if( nullptr != m_timer ) { m_timer.onUpdate(); } } } sTimerPrivate; // Timer::Timer() : m_private( &sTimerPrivate ) { } -> ist zwar aktuell auf 1 Timer-Objekt beschränkt, lässt sich aber mit nem ITimer*-Array in Timer_Private erweitern bzw. darin auch hinsichtlich Update-Raten filtern. z.B. dass der bei CTimer::RegisterPeriodicHandler( &Timer_Private::onUpdate ); registriete statische Handler alle 100ms gepollt wird und dort noch mal auf x Sekunden erweitert werden kann.
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.