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.
> 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
virtualvoidonTimer()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:
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:
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
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.