Forum: PC-Programmierung c++ static methoden


von Olaf (Gast)


Lesenswert?

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

von Klaus W. (mfgkw)


Lesenswert?

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
von Olaf (Gast)


Lesenswert?

> 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 Programmierer (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

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
von Programmierer (Gast)


Lesenswert?

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.

von Tom (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Programmierer (Gast)


Lesenswert?

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 
;-)

von Klaus W. (mfgkw)


Lesenswert?

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...

von Olaf (Gast)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

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

von Klaus W. (mfgkw)


Lesenswert?

Olaf schrieb:
> (void*)

Darf man gerne vermeiden, wenn möglich.

von tut nix zur Sache (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.