Forum: Mikrocontroller und Digitale Elektronik statische Polymorphie per CRTP in Embedded Systemen


von Christian K. (mick-roc)


Lesenswert?

Hallo zusammen, ich untersuche gerade verschiedene Designpatterns für 
die Nutzung von C++ in der Embedded Software Entwicklung (ohne OS). 
Vorallem das Thema statische Polymorphie per CRTP 
(https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) 
klingt sehr interessant, allerdings habe ich hierzu folgende 
Frage/Problem:
Bei CRTP ist das Basis-Interface keine Klasse mit virtuellen Methoden, 
sondern ein Template, das als Templateparameter die abgeleitete 
Implementierungs-Klasse selbst erhält, sodass die Basis ihre Ableitungen 
kennt. An sich ein sehr kleveres Desing, allerdings bedeutet das doch 
auch, dass andere Klassen/Funktionen, die das Interface verwenden wollen 
(als Parameter erwarten) ebenfalls Templates sein müssen, oder?
1
/* 0 = use classic virtual polymorphism */
2
/* 1 = use CRTP polymorphism */
3
#define CRTP_EN 1
4
5
/* start of STM32 HAL simulationL */
6
#include <iostream>
7
#include "stdint.h"
8
9
typedef char GPIO_TypeDef;
10
enum GPIO_PinState {GPIO_PIN_SET, GPIO_PIN_RESET};
11
#define GPIO_PIN_0 0
12
#define GPIO_PIN_1 1
13
#define GPIO_PIN_2 2
14
char gpioA = 'A';
15
GPIO_TypeDef *GPIOA = &gpioA;
16
17
void HAL_GPIO_WritePin(GPIO_TypeDef *port, uint16_t pin, GPIO_PinState state) {
18
  (void)port;
19
  std::cout << "set pin " << *port << pin << (state == GPIO_PIN_SET ? " high" : " low") << std::endl;
20
}
21
/* end of STM32 HAL simulation */
22
23
/* generic interface */
24
#if(CRTP_EN)
25
template <typename Impl>
26
struct DigitalOut {
27
  void setHigh(bool high) {static_cast<Impl&>(*this).setHigh(high);}
28
};
29
#else
30
struct DigitalOut {
31
  virtual ~DigitalOut() {}
32
  virtual void setHigh(bool high) = 0;
33
};
34
#endif
35
36
/* user of generic interface */
37
enum color {off, red, green, blue, yellow, magenta, cyan, white};
38
39
#if(CRTP_EN)
40
template <typename ImplR, typename ImplG, typename ImplB>
41
class RgbLed {
42
  DigitalOut<ImplR> *r;
43
  DigitalOut<ImplG> *g;
44
  DigitalOut<ImplB> *b;
45
public:
46
  RgbLed(DigitalOut<ImplR> &r, DigitalOut<ImplG> &g, DigitalOut<ImplB> &b) : r(&r), g(&g), b(&b) {}
47
#else
48
class RgbLed {
49
  DigitalOut *r, *g, *b;
50
public:
51
  RgbLed(DigitalOut &r, DigitalOut &g, DigitalOut &b) : r(&r), g(&g), b(&b) {}
52
#endif
53
  void setColor(color toSet) {
54
    switch(toSet) {
55
    default: /* no break */
56
    case off:
57
      r->setHigh(false);
58
      g->setHigh(false);
59
      b->setHigh(false);
60
      break;
61
    case yellow:
62
      r->setHigh(true);
63
      g->setHigh(true);
64
      b->setHigh(false);
65
      break;
66
    case white:
67
      r->setHigh(true);
68
      g->setHigh(true);
69
      b->setHigh(true);
70
      break;
71
    /* all other color cases ... */
72
    }
73
  }
74
};
75
76
/* interface implementation */
77
#if(CRTP_EN)
78
struct DigitalOutStm32 : public DigitalOut<DigitalOutStm32> {
79
  void setHigh(bool high) {HAL_GPIO_WritePin(port, pin, high ? GPIO_PIN_SET: GPIO_PIN_RESET);}
80
#else
81
struct DigitalOutStm32 : public DigitalOut {
82
  virtual ~DigitalOutStm32() {} /* not required for CRTP... */
83
  virtual void setHigh(bool high) { HAL_GPIO_WritePin(port, pin, high ? GPIO_PIN_SET: GPIO_PIN_RESET);}
84
#endif
85
  DigitalOutStm32(GPIO_TypeDef *port, uint16_t pin) : port(port), pin(pin) {}
86
protected:
87
  GPIO_TypeDef *port;
88
  uint16_t      pin;
89
};
90
91
/* application */
92
int main(void) {
93
  DigitalOutStm32 doR(GPIOA, GPIO_PIN_0);
94
  DigitalOutStm32 doG(GPIOA, GPIO_PIN_1);
95
  DigitalOutStm32 doB(GPIOA, GPIO_PIN_2);
96
#if(CRTP_EN)
97
  RgbLed<DigitalOutStm32, DigitalOutStm32, DigitalOutStm32> led(doR, doG, doB);
98
#else
99
  RgbLed                                                    led(doR, doG, doB);
100
#endif
101
  while(1) {
102
    led.setColor(off);
103
    led.setColor(yellow);
104
    led.setColor(white);
105
    break; /* just for demonstration, usually infinity loop... */
106
  }
107
  return 0;
108
}

Hier zum Rumspielen: http://cpp.sh/8zgc3

Das Beispiel zeigt eine RgbLed-Klasse, die drei einfache digitale 
Ausgänge erwartet, an welchen farbige LEDs (Rot, Grün, Blau) 
angeschlossen sein sollten. Die Klasse bietet dann eine 
setColor()-Funktion, die die Farbmisching übernimmt. Hierzu bezieht sich 
die Klasse auf DigitalOut, als generisches Interface. DigitalOutStm32 
stellt dann die konkrete, plattformspezifische Interface-Implementierung 
für die STM32 HAL da.

Wie man sieht erfordert die CRTP Variante, dass nicht nur DigitalOut ein 
Template ist, sondern auch RgbLed und theoretisch auch alles was RgbLed 
verwenden möchte (die Template-Argumentliste wird potenziell riesig!) 
...

Mir gefällt die Möglichkeit auf virtual verzichten zu können, aber ich 
möchte nicht alle Treiber und Bibliotheken als Templates verfassen 
müssen.

Hat jemand eine Idee? Habe ich CRTP falsch verstanden? Hat jemand 
Erfahrung mit diesem Designpattern?

Danke vorab!

von Christian K. (mick-roc)


Lesenswert?

Hat keiner eine Idee, oder ist die Frage zu schnell untergegangen?

von Dr. Sommer (Gast)


Lesenswert?

Christian K. schrieb:
> allerdings bedeutet das doch auch, dass andere Klassen/Funktionen, die
> das Interface verwenden wollen (als Parameter erwarten) ebenfalls
> Templates sein müssen, oder?

Ja, natürlich, es sei denn sie brauchen nur eine bestimmte Instanzierung 
des Templates. Bei statischer Verknüpfung zwischen interface und dessen 
Nutzer müssen halt alle Nutzer es statisch kennen und daher ggf. 
Templates werden. Das ist der Preis wenn man kein "virtual" will...

von Peter D. (peda)


Lesenswert?

Ich staune immer wieder, welchen riesen Wust an Code man in C++ für die 
allerkleinsten Aufgaben erzeugen kann.
Ich bin vermutlich schon zu alt für C++.
Ich komme mir beim Lesen immer vor, wie Buchbinder Wanninger. Eine 
Instanz zeigt auf die nächste, bis man gar nicht mehr weiß, worum es 
überhaupt geht.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Bei doppelt verketteten Listen kann eine Instanz auch auf die vorherige 
zeigen!

von Possetitjel (Gast)


Lesenswert?

Peter D. schrieb:

> Ich staune immer wieder, welchen riesen Wust an Code man
> in C++ für die allerkleinsten Aufgaben erzeugen kann.

Hauptziel von C++ ist Abstraktion. Ein korrekt
funktionierendes LESBARES Programm ist eine billigend
in Kauf genommene Nebensache.

von Tom (Gast)


Lesenswert?

Peter D. schrieb:
> riesen Wust an Code man in C++ für die
> allerkleinsten Aufgaben

Ein Problem, das so komplex ist, dass abstraktere Programmiertechniken 
am Ende einfacher (und weniger fehleranfällig sind) sind, eignet sich 
schlecht für ein Mini-Beispiel, das zum Verstehen und im Forum fragen 
geeignet ist.

Die Diskusion läuft hier immer ungefähr so ab:
A: "Ich habe Technik XY entdeckt, mit der man nach 2h Einarbeitung 
riesige Probleme flexibel und fehlersicher bearbeiten kann. Hier ein 
Miniatur-Beispiel anhand eines LED-Blinkers, wie genau könnte man das am 
besten auf das Feature UVW erweitern?"

B: "Du idiot, led blinker sind in retro-C viel einfacher LOL."

C: "Das ist alles viel zu langsam, in Assembler läuft die Blinkschleife 
mit 2 Takten weniger."

von Dr. Sommer (Gast)


Lesenswert?

Gerade so etwas wie CRTP geht in C nicht - es ist effizient und 
flexibel, in C muss man sich zwischen einem von beiden entscheiden. 
Indem man als CRTP-Parameter eine Klasse mit virtuellen Funktionen 
übergibt kann man die Runtime-Polymorphie sogar nachträglich nachrüsten, 
und hat somit alle Freiheiten. Super lesbar ist es nicht, aber je nach 
Situation (wenn man die Flexibilität behalten will) sogar effizienter 
als C. Wenn Lesbarkeit an 1. Stelle steht nimmt man Python oder Java, 
aber bestimmt nicht C.

Peter D. schrieb:
> Ich bin vermutlich schon zu alt für C++.

C++ gibt es seit über 30 Jahren. Du hattest genug Gelegenheit es zu 
lernen. Wenn du zu faul/kurzsichtig dafür warst, bist du selber schuld, 
und es gibt dir keine Rechtfertigung sinnlos darüber zu lästern.

von S. R. (svenska)


Lesenswert?

Die Peanut-Gallery antwortet:

Dr. Sommer schrieb:
> Wenn du zu faul/kurzsichtig dafür warst, bist du selber schuld,
> und es gibt dir keine Rechtfertigung sinnlos darüber zu lästern.

Und C++98 und C++17 sind sich auch so unsagbar ähnlich, besonders mit 
all den neuen Features für embedded. Wer das in den 80ern gelernt hat, 
ist voll auf dem neuesten Stand!

von Christian K. (mick-roc)


Lesenswert?

Ok, dachte schon ich bin im falschen Forumsbereich... danke an alle für 
die Antworten!

Tom schrieb:
> Ein Problem, das so komplex ist, dass abstraktere Programmiertechniken
> am Ende einfacher (und weniger fehleranfällig sind) sind, eignet sich
> schlecht für ein Mini-Beispiel, das zum Verstehen und im Forum fragen
> geeignet ist.

Genau das! Vielleicht hätte ich auch den Code nicht mit Compiler-Switch 
verfassen sollen. Dachte das hebt die Unterschiede hervor...

Dr. Sommer schrieb:
> Wenn du zu faul/kurzsichtig dafür warst, bist du selber schuld,
> und es gibt dir keine Rechtfertigung sinnlos darüber zu lästern.

Bitte sachlich bleiben. Oftmals ist es nicht mal die Entscheidung des 
Entwicklers. Ich arbeite gerade selbst daran (daher die Untersuchung...) 
Vorurteile gegenüber C++ abzubauen, damit es bei uns eingesetzt werden 
kann.

Das Beispiel ist tatsächlich nicht mal so weit hergeholt. Für eine 
plattformübergreifende Entwicklung (heute STM32, morgen PIC, ...) kann 
selbst bei einfachen digital IOs eine polymorphe API sinnvoll sein um 
abstraktere Treiber/Funktionen/Bibliotheken zu entwickeln. Der Overhead 
darf dabei allerdings nicht zu groß werden (wobei man den bei einer 
gleichwertigen C-Lösung auch hätte!)

Dr. Sommer schrieb:
> Indem man als CRTP-Parameter eine Klasse mit virtuellen Funktionen
> übergibt kann man die Runtime-Polymorphie sogar nachträglich nachrüsten,
> und hat somit alle Freiheiten.

Wie meinst du das genau? Unter dem CRTP-Parameter verstehe ich die 
abgeleitete Klasse, die ich dem Basis-Template zur Spezialisierung beim 
Erben übergebe (in meinem Beispiel DigitalOutStm32). Das ändert dann 
aber nichts an der Tatsache, dass das Interface ein Template ist, sodass 
der Benutzer (hier: RgbLed) auch eins sein muss. Oder verstehe ich dich 
falsch?

von Fitzebutze (Gast)


Lesenswert?

Und täglich grüsst das Murmeltier...
Diese Threads zum Thema C++ und Embedded tauchen immer wieder auf und 
bleiben letztendlich meist von akademischer Natur. Im produktiven 
Einsatz bringt meistens der vermutete Vorteil von C++ wenig, bzw. kann 
man oft beobachten, wie sich eine Menge Programmierer damit verzetteln 
und die Probleme dann unbegrenzt kaskadieren, wenn der 
template-generierte Code debuggt werden muss. Da hat sich in 30 Jahren 
C++ nicht viel geändert. Der Trugschluss ist, dass der Template-Wust ein 
gut designtes 'OS' (oder Low-Level Kernel-Framework) nicht ersetzt.
Low-Level-Kram wie Register, gemultiplexte GPIOs usw. sind von der 
Komplexität noch so gering, dass man sie nicht mit C++ 
kaputtabstrahieren sollte. Der so generierte Code ist seltenst 
minimal-simpel, und wenn's um Safety und Beweisbarkeit geht, fliegen 
Templates schon mal raus. Hat schon seine Gründe, dass die meisten OS 
noch in strikt 'retro'-C geschrieben sind und sich da so bald auch nix 
dran ändert.
Bei komplexeren Themen im User-Space wie GUIs oder Objekt-Pools wo es 
egal ist, wieviel Code nun genau generiert wird, haben Templates schon 
ihre Berechtigung und man spart u.U. viel Zeit. Bei Low-Level embedded 
ist leider (aus Erfahrung) die Portabilität von C++-basierten Lösungen 
meist für die Katz und die wunderschöne Theorie bleibt dann eben 
akademische Spielerei.

von S. R. (svenska)


Lesenswert?

Christian K. schrieb:
> Das Beispiel ist tatsächlich nicht mal so weit hergeholt. Für eine
> plattformübergreifende Entwicklung (heute STM32, morgen PIC, ...) kann
> selbst bei einfachen digital IOs eine polymorphe API sinnvoll sein um
> abstraktere Treiber/Funktionen/Bibliotheken zu entwickeln.

Wo ist eigentlich der große Vorteil gegenüber einem 
plattformspezifischen C-Header, der für GPIOs einfache "static 
inline"-Funktionen deklariert? Eine handvoll Taktzyklen?

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

S. R. schrieb:
> Christian K. schrieb:
>> Das Beispiel ist tatsächlich nicht mal so weit hergeholt. Für eine
>> plattformübergreifende Entwicklung (heute STM32, morgen PIC, ...) kann
>> selbst bei einfachen digital IOs eine polymorphe API sinnvoll sein um
>> abstraktere Treiber/Funktionen/Bibliotheken zu entwickeln.
>
> Wo ist eigentlich der große Vorteil gegenüber einem
> plattformspezifischen C-Header, der für GPIOs einfache "static
> inline"-Funktionen deklariert? Eine handvoll Taktzyklen?

Mann könnte verschiedene GPIO Implementierungen für jede Farbe der RGB 
LED verwenden. Z.B. einen direkt am STM, einen an einem per SPI 
angebundenen Schieberegister und einen der per WLAN angesprochen wird.

Ob solche Ansätze so weit unten (in der Treiberschicht) wirklich 
notwendig sind und auch was bringen? Kann ich mir gerade kein Urteil 
drüber bilden. Kann was bringen aber ob es den Aufwand gegenüber 
klassischer Polymorphie wert ist? Auf kleinen Systemen (uC mit schmalen 
RTOS oder Bard metal) hat man meist die Notwendigkeit nicht und auf 
größeren (uP mit richtigem OS) tut die zusätzliche Indirektion einer 
virtuellen Methode nicht weh. Aber grundsätzlich finde ich die 
Möglichkeiten die C++ bietet interessant und will sie im uC Bereich auch 
Mal evaluieren.


Matthias

von Wilhelm M. (wimalopaan)


Lesenswert?

Christian K. schrieb:

> Wie man sieht erfordert die CRTP Variante, dass nicht nur DigitalOut ein
> Template ist, sondern auch RgbLed und theoretisch auch alles was RgbLed
> verwenden möchte

im Prinzip ja (s.u.)

> Mir gefällt die Möglichkeit auf virtual verzichten zu können, aber ich
> möchte nicht alle Treiber und Bibliotheken als Templates verfassen
> müssen.

Warum nicht. Das hat auch Vorteile.

> Hat jemand eine Idee? Habe ich CRTP falsch verstanden? Hat jemand
> Erfahrung mit diesem Designpattern?

CRTP hat im wesentlichen zwei getrennte Einsatzgebiete:

1) Mixin (from above)
2) stat. Polymorphie "Helper"

zu 1)

Hier wird die Funktionalität des Subtyps durch den Basistyp einfach 
erweitert, wobei der Basistyp auf die Elemente des Subtyps zugreift. 
M.E. sollte man dazu aber non-member non-friends vorziehen. Vererbung 
wegen Code-Reuse ist immer einer schlechte Idee. So auch hier, denn es 
gilt hier nicht die ist-ein Beziehung zwischen Sub- und Basis-Typ.

zu 2)

Wie Du schon bemerkt hast, müssen Clienten, die die stat. Polymorphie 
nutzen wollen, natürlich templates sein. In C++ bedeutet stat. 
Polymorphie nun einmal nichts anderes als generischer Code via 
templates.
Klienten stellen an die verwendeten template-Typ-Parameter implizite 
Typanfordernungen, d.h. man fordert, dass von T eine bestimmte Operation 
unterstützt wird. Ist das nicht der Fall, bricht die Kompilation ab. 
Diese Anforderung kann via CRTP explizit gemacht werden. Auch hier: 
verwende ich im CRTP einen Subtyp, der die (impliziten) Anforderungen 
nicht erfüllt, kompiliert es nicht. Der Fehler taucht nur an anderer 
Stelle auf.

Letztendlich geht es darum, Typanforderungen an template-Typ-Parameter 
irgendwie explizit zu machen.

Aber genau dazu sind Concepts erfunden worden: mit ihnen kann ich 
Anforderungen an Typ-Parameter explizit formulieren. Sind diese 
Anforderungen nicht erfüllt, so bricht natürlich auch die Kompilation 
ab, aber nun an der richtigen Stelle und mit viel besseren 
Fehlermeldungen.

(BTW: die Idee zu concepts ist (fast) so alt wie die template-Mechanik 
selbst, wurden aber erst sehr spät als Entwurf akzeptiert als 
concepts-ts und befinden sich nun auf dem Weg in C++20.)

Zudem kann ich mit concepts, die sich in ihren requirements nicht 
überdecken, Klassentemplates elegant partiell spezialisieren, was dann 
wie ein "overload" von Klassen(!) aussieht.

concepts sind als concepts-ts im gcc seit 6.3 mit drin und werden 
offiziell in C++20 enthalten sein.

Für mich bedeutet das als Konsequenz:

zu 1) mixin from above: war m.E. noch nie eine sooo tolle Idee.

zu 2) stat. Polymorphie "Helper": nur eine "historische" Krücke, deren 
Einsatz m.E. überschätz wurde. Die richtige Lösung heisst concepts.

Zu Deinem use-case: mach in Deinem RgbLed-template die Outputs einfach 
zu template-Paramteren (btw.: da sollte doch ein Parameter reichen). Du 
brauchst da kein CRTP (wie gesagt: CRTP so eingesetzt verschiebt nur den 
Fehlerort).

von Peter D. (peda)


Lesenswert?

Christian K. schrieb:
> Genau das! Vielleicht hätte ich auch den Code nicht mit Compiler-Switch
> verfassen sollen. Dachte das hebt die Unterschiede hervor...

Das ist mir etwas später aufgefallen, daß der Code quasi doppelt ist.
Interessant finde ich aber das eigentliche Problem, wie man IO-Zugriffe 
intelligent kapseln und skalieren könnte. Dafür konnte ich jedoch keine 
Lösung darin erkennen.

Wenn man z.B. 20 LEDs damit steuern will, kann es ja wohl nicht sein, 
daß man 20 verschiedene setColor() schreiben muß. Auch sollte die Lösung 
so flexibel sein, daß sie nicht nur für direkte IO-Pins geht, sondern 
auch für virtuelle, die z.B. über einen MAX7219 ausgegeben werden.
In plain C würde ich dafür ein Array mit Pinnummern erstellen, was die 
Zuordnung der LEDs vornimmt. Entweder mit 60 Einträgen für jede LED oder 
mit 20 Einträgen, wenn alle RGB-LEDs auf je 3 aufeinanderfolgende Pins 
verdrahtet werden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:

> Wenn man z.B. 20 LEDs damit steuern will, kann es ja wohl nicht sein,
> daß man 20 verschiedene setColor() schreiben muß.

Nein, wieso kommst Du darauf?

> Auch sollte die Lösung
> so flexibel sein, daß sie nicht nur für direkte IO-Pins geht, sondern
> auch für virtuelle, die z.B. über einen MAX7219 ausgegeben werden.

Kann man machen.

> In plain C würde ich dafür ein Array mit Pinnummern erstellen, was die
> Zuordnung der LEDs vornimmt. Entweder mit 60 Einträgen für jede LED oder
> mit 20 Einträgen, wenn alle RGB-LEDs auf je 3 aufeinanderfolgende Pins
> verdrahtet werden.

In C++ würde man eher ein variadisches Template nehmen. Einmal mit dem 
Treiber für den z.B. MAX xxxx und einmal für die WS2812 oder was auch 
immer als Template-Typ-Parameter. Die Treiber selbst sind natürlich auch 
wieder templates mit den Pins als Parameter und was sonst noch so 
gebraucht wird. Die Pins sind auch wieder templates, mit den Ports und 
Pin-Nummern als Parameter, etc. ...

Geht alles ganz gut und ist 0-overhead. Mit dem Vorteil, dass man zur 
Compilezeit aus so etwas wie einen Konfigchecker machen kann. Ist 
beispielsweise bei den PinMuxern (SAM, XMega, STM) ganz praktisch, weil 
nicht alle internen Pads der internen Peripherie auf beliebige Pins 
gelegt werden kann. So vermeidet man viele Laufzeitfehler.

von Vincent H. (vinci)


Lesenswert?

Peter D. schrieb:
> Wenn man z.B. 20 LEDs damit steuern will, kann es ja wohl nicht sein,
> daß man 20 verschiedene setColor() schreiben muß. Auch sollte die Lösung
> so flexibel sein, daß sie nicht nur für direkte IO-Pins geht, sondern
> auch für virtuelle, die z.B. über einen MAX7219 ausgegeben werden.
> In plain C würde ich dafür ein Array mit Pinnummern erstellen, was die
> Zuordnung der LEDs vornimmt. Entweder mit 60 Einträgen für jede LED oder
> mit 20 Einträgen, wenn alle RGB-LEDs auf je 3 aufeinanderfolgende Pins
> verdrahtet werden.

Muss er ja auch nicht? 20 Instanzen der Klasse tuns auch...?

Und natürlich ginge das mit virtuellen Pins auch. setHigh darf ja ruhig 
was anderes tun als nur a Register zu schreiben...

von Peter D. (peda)


Lesenswert?

Vincent H. schrieb:
> Muss er ja auch nicht? 20 Instanzen der Klasse tuns auch...?

Das meinte ich doch, man hat den 20-fachen Schreibaufwand und den 
20-fachen Code.
Ich würde es gerne auf einen Code, d.h. eine Funktion reduzieren, die 
die unterschiedlichen Bitnummern aus einer Tabelle entnimmt. Oder wenn 
man die LEDs entsprechend verdrahtet, die Bitnummern aus einer 
Vorschrift errechnet.

von Carl D. (jcw2)


Lesenswert?

Peter D. schrieb:
> Vincent H. schrieb:
>> Muss er ja auch nicht? 20 Instanzen der Klasse tuns auch...?
>
> Das meinte ich doch, man hat den 20-fachen Schreibaufwand und den
> 20-fachen Code.
> Ich würde es gerne auf einen Code, d.h. eine Funktion reduzieren, die
> die unterschiedlichen Bitnummern aus einer Tabelle entnimmt. Oder wenn
> man die LEDs entsprechend verdrahtet, die Bitnummern aus einer
> Vorschrift errechnet.

Die wichtige Frage wäre: wann?
Zur Laufzeit, das wäre die C-Variante (auch Arduino macht das so)
Oder zur Compile-Zeit, mit dem Ergebnis "SBI 0x19,1".

Hängt also davon ab, wo man knappe Resourcen hat.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Vincent H. schrieb:
>> Muss er ja auch nicht? 20 Instanzen der Klasse tuns auch...?

Es brauchen ja auch gar keine Objekte zu sein. Template-Instanzen tuns 
ja auch. Denn die Konfig ist ja eher statisch.

> Ich würde es gerne auf einen Code, d.h. eine Funktion reduzieren, die
> die unterschiedlichen Bitnummern aus einer Tabelle entnimmt. Oder wenn
> man die LEDs entsprechend verdrahtet, die Bitnummern aus einer
> Vorschrift errechnet.

Ja, kann man doch. Statt einer Tabelle kann man auch einfach eine 
Typ-Liste der Pins o.ä. nehmen:
1
led1 = Pin<PortA, 1>;
2
led2 = Pin<PortB, 2>;
3
led3 = Pin<PortC, 3>;
4
5
using ledController = MAX1234<led1, led2, led3>;
6
7
ledController::doSomething();

von Possetitjel (Gast)


Lesenswert?

Peter D. schrieb:

> Ich würde es gerne auf einen Code, d.h. eine Funktion
> reduzieren, die die unterschiedlichen Bitnummern aus
> einer Tabelle entnimmt. Oder wenn man die LEDs
> entsprechend verdrahtet, die Bitnummern aus einer
> Vorschrift errechnet.

Dazu würde ich Anleihen aus der SPS-Technik aufnehmen:
Dein Bitfeld im RAM bildet das "Prozessabbild der
Ausgänge", das den gewünschten Zustand der Leuchtdioden
aufnimmt. Timergesteuert werden die Treiber aufgerufen,
die die relevanten Bits aus dem Prozessabbild der Ausgänge
zusammenklauben und auf die reale Hardware schreiben.
(Dafür lassen sich sicher auch wiederverwendbare Teil-
funktionen schaffen, beispielsweise das effiziente
Bestimmen vom Bitpermutationen, das Serialisieren o.ä.)

Dadurch ist die Anwendungslogik des Programmes komplett
von den Details der Hardware-Ansteuerung entkoppelt;
letztere stecken nur in den Funktionen, die die "Hardware-
treiber" bilden.

Ob ein Laufzeitnachteil entsteht, wäre zu untersuchen,
denn es besteht weder im "Anwendungsprogramm" noch in
den "Treibern" ein Zwang, bitseriell zu arbeiten (außer
natürlich, der Treiber muss tatsächlich "von Hand"
serialisieren).

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Ja, kann man doch. Statt einer Tabelle kann man auch einfach eine
> Typ-Liste der Pins o.ä. nehmen:
>
> led1 = Pin<PortA, 1>;
> led2 = Pin<PortB, 2>;
> led3 = Pin<PortC, 3>;

Was habe ich denn damit gewonnen?
Bei 20 RGB-LEDs müßte diese Typ-Liste ja bis led60 gehen. Und dazu noch 
die 20 Funktionen zum Setzen.
Ich nehme da nur eine Funktion:
1
bool setColor( uint8_t led_no, uint8_t color);

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Ja, kann man doch. Statt einer Tabelle kann man auch einfach eine
>> Typ-Liste der Pins o.ä. nehmen:
>>
>> led1 = Pin<PortA, 1>;
>> led2 = Pin<PortB, 2>;
>> led3 = Pin<PortC, 3>;
>
> Was habe ich denn damit gewonnen?
> Bei 20 RGB-LEDs müßte diese Typ-Liste ja bis led60 gehen. Und dazu noch
> die 20 Funktionen zum Setzen.
> Ich nehme da nur eine Funktion:
>
1
bool setColor( uint8_t led_no, uint8_t color);

Und wo bitte hast Du jetzt Deine Pins der Leds definiert? Irgendwo wirst 
Du ja irgendetwas definieren müssen. Zeig doch mal, wie Du die led_no 
auf eine Port/Pin-Kombination abbildest.

: Bearbeitet durch User
von MitLeserin (Gast)


Angehängte Dateien:

Lesenswert?

Die Problematik besteht darin, dass man die Zusammenhänge und die 
Vorteile erst versteht, wenn man sich einen einigermassen umfassenden 
Überblick über C++ und <templates> verschafft hat.

C++ Bücher sind für Praktiker von der elektrotechnischen Seite her oft 
schwer nachzuvollziehen, weil zu abstrakt und zu Informatik lastig 
ausformuliert.

Beispiele (compilierbar) wären hilfreich, sind eher selten zu finden.

Oben angefügt ein Code-Snippet zur Illustration, das zeigen soll, wie 
C++ eventuell doch gewisse positive Eigenschaften hätte.

Alexandra

von Einer K. (Gast)


Angehängte Dateien:

Lesenswert?

MitLeserin schrieb:
> Beispiele (compilierbar) wären hilfreich, sind eher selten zu finden.

Bin ja voll auf Arduino....
Beispiele satt..

Aber Pin Gedöns im OOP Mäntelchen, eher selten.

(falls die Libraries gesehen werden wollen, kann ich sie gerne 
nachliefern)

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Und wo bitte hast Du jetzt Deine Pins der Leds definiert?

Wie schon gesagt, als Array (Portadresse, Bitmaske).
Oder als Vorschrift, z.B. alle LEDs liegen hintereinander als 3 Bit je 
RGB im Memory.
Hier mal als Vorschrift:
1
#include <stdint.h>
2
3
uint8_t max7219_data[8+1];
4
5
void update_max7219( void );
6
7
void setColor( uint16_t led_no, uint16_t color )
8
{
9
  uint16_t *ptr;
10
  uint16_t mask;
11
  led_no *= 3;
12
  mask = 7 << (led_no & 7);
13
  color = (color & 7) << (led_no & 7);
14
  ptr = (uint16_t *)(max7219_data + (led_no >> 3));
15
  *ptr = ((*ptr & ~mask) | color);
16
  update_max7219();
17
}

von Peter D. (peda)


Lesenswert?

MitLeserin schrieb:
> Oben angefügt ein Code-Snippet zur Illustration, das zeigen soll, wie
> C++ eventuell doch gewisse positive Eigenschaften hätte.

Das werde ich mir mal näher ansehen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Und wo bitte hast Du jetzt Deine Pins der Leds definiert?

> Hier mal als Vorschrift:
>
1
> #include <stdint.h>
2
> 
3
> uint8_t max7219_data[8+1];
4
> 
5
> void update_max7219( void );
6
> 
7
> void setColor( uint16_t led_no, uint16_t color )
8
> {
9
>   uint16_t *ptr;
10
>   uint16_t mask;
11
>   led_no *= 3;
12
>   mask = 7 << (led_no & 7);
13
>   color = (color & 7) << (led_no & 7);
14
>   ptr = (uint16_t *)(max7219_data + (led_no >> 3));
15
>   *ptr = ((*ptr & ~mask) | color);
16
>   update_max7219();
17
> }
18
>


Naja, wo wird denn hier max7219_data initialisiert? Was bedeuten Die 
Daten in dem Array? In meinen Augen völlig undurchsichtig. Die gesamte 
Konfig ist also irgendwo im Code versteckt ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> MitLeserin schrieb:
>> Oben angefügt ein Code-Snippet zur Illustration, das zeigen soll, wie
>> C++ eventuell doch gewisse positive Eigenschaften hätte.
>
> Das werde ich mir mal näher ansehen.

Ähnliches habe ich hier auch schon ein paarmal gepostet. Nur leider 
schauen sich die Leute das erstens nicht richtig an bzw. verstehen 
nichts über templates bzw. TMP und beschweren sich dann über die 
Unlesbarbeit. Viele wissen auch nicht, was Metafunktionen sind oder wozu 
man Typabbildungen braucht oder wie man zur Compilezeit mit Typen (nicht 
mit Werten) rechnen kann. Und dann wird sich beschwert, dass es 
kompliziert sei. Dazu gehört eben auch, dass man sich damit von Grund 
auf einmal beschäftigt. Das template-System ist Turing-vollständig, aber 
eben eine eigene Sprache in der Sprache. Ich kann nur sagen, es lohnt 
sich sehr, damit auseinander zu setzen.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Ähnliches habe ich hier auch schon ein paarmal gepostet.

Nur ist das aber mal ein praktisches Beispiel, wo auch der ungeübte 
einen roten Faden erkennen kann.
Deine Beispiele kommen immer hoch theoretisch daher, d.h. man muß C++ 
schon perfekt können, um sie zu verstehen.

von Carl D. (jcw2)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Ähnliches habe ich hier auch schon ein paarmal gepostet.
>
> Nur ist das aber mal ein praktisches Beispiel, wo auch der ungeübte
> einen roten Faden erkennen kann.
> Deine Beispiele kommen immer hoch theoretisch daher, d.h. man muß C++
> schon perfekt können, um sie zu verstehen.

Wobei man durch McuCpp leicht erschreckt werden kann, denn es basiert 
auf C++03, wo man vieles was in C++17 als einfacher Code daherkommt, 
kompliziert als Meta-Programm schreiben muß. Ich nenne nur mal 
"parameter packs" als Sprachkonstrukt vs "loki"-Template-Halden.
Von dem, was der Benutzer der Bibliothek hinschreibt, ist das aber fast 
auf aktuellem Stand.

Der wesentliche Unterschied zwischen der "PinNummer -> Addressen/Maske" 
Variante (z.B. Arduino) und McuCpp mit Einem Typ, der mehr oder weniger 
die selbe Information beinhaltet, ist daß im letzten Fall der Compiler 
diese Info verarbeitet und nicht nur eine Laufzeitroutine, die halt 
nicht mal per JIT an die konkrete Situation angepaßten Objektcode 
erzeugt.
Wenn z.B. auf einem Chip die virtuellen Pins 1,2,3 in einem Port 
aufsteigend hintereinander liegen, dann entsteht kompakterer Code, wenn 
diese Wild auf Ports verteilt sind, dann wird's komplizierter, die 
Arduino-Variant "zur Laufzeit" ergibt immer universell funktionierenden 
Code.
Nebenbei kann man mit dem Typ Pin<PortA,2> nur machen, was man dafür 
vorgesehen hat. Hilft gegen das gelegtlich zu sehende setzen eines Bits, 
das es in dem angesprochenen Port gar nicht gibt, es aber nur ein Macro 
ist, das eine INT-Konstante liefert und der Port nur ein Zeiger auf eine 
8-Bit-Speicherstelle. (typische Frage: warum geht der Timer 
nicht/falsch? Antwort: Eventuell weil TCCR2A kein Bit CS01 hat?!)

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Ähnliches habe ich hier auch schon ein paarmal gepostet.
>
> Nur ist das aber mal ein praktisches Beispiel, wo auch der ungeübte
> einen roten Faden erkennen kann.
> Deine Beispiele kommen immer hoch theoretisch daher, d.h. man muß C++
> schon perfekt können, um sie zu verstehen.

Stimmt nicht so ganz: genau so ein Beispiel hatte ich schon mehrfach 
gepostet. Ist vllt an Dir vorbeigegangen ...

Der theoretische Eindruck entsteht vielleicht, weil ich versuche, auch 
immer die richtigen Begriffe zu verwenden. Z.B. spreche ich auch vom 
Instanziieren eines Templates, was etwas anderes ist, als das 
Instanziieren eines Typs (vulgo Klasse), spreche von 
template-typ-parametern, template-template-parametern oder 
template-nicht-typ-parametern. Das sind einfach die offiziellen 
Begriffe. Da aber sprache Ausdruck der Gedanken sind, finde ich es 
wichtig, diese Begriffe zu verwenden. Auch der Begriff Meta-Funktion ist 
ein Begriff dieser Kategorie. Und ich glaube kaum, dass dieser Begriff 
allen hier bekannt ist. Man könnte ihn aber auch mal nachlesen ...

von MitLeserin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Der theoretische Eindruck entsteht vielleicht, weil ich versuche, auch
> immer die richtigen Begriffe zu verwenden...
> Da aber sprache Ausdruck der Gedanken sind, finde ich es
> wichtig, diese Begriffe zu verwenden

Das ist löblich und auch gut so. Bloss wenn der Leser das KnowHow noch 
nicht hat, weiss er nicht was der Verfasser meint und kann die Gedanken 
nicht nachvollziehen. Das Vorurteil gegen C++ bleibt bestehen.

Carl D. schrieb:
> Wobei man durch McuCpp leicht erschreckt werden kann, denn es basiert
> auf C++03, wo man vieles was in C++17 als einfacher Code daherkommt,
> kompliziert als Meta-Programm schreiben muß. Ich nenne nur mal
> "parameter packs" als Sprachkonstrukt vs "loki"-Template-Halden.
> Von dem, was der Benutzer der Bibliothek hinschreibt, ist das aber fast
> auf aktuellem Stand.

Siehe oben:
Bitte an compilierbaren Beispielen detaillierter erklären. Wenn möglich 
thematisch separiert, jeweils ein c++03 snippet vs. aequivalentes c++17 
snippet.

Ich würde mich freuen..

von Wilhelm M. (wimalopaan)


Lesenswert?

MitLeserin schrieb:
> Wilhelm M. schrieb:
>> Der theoretische Eindruck entsteht vielleicht, weil ich versuche, auch
>> immer die richtigen Begriffe zu verwenden...
>> Da aber sprache Ausdruck der Gedanken sind, finde ich es
>> wichtig, diese Begriffe zu verwenden
>
> Das ist löblich und auch gut so. Bloss wenn der Leser das KnowHow noch
> nicht hat, weiss er nicht was der Verfasser meint und kann die Gedanken
> nicht nachvollziehen. Das Vorurteil gegen C++ bleibt bestehen.

Wir leben im Google-Zeitalter: wenn ich nicht weiß, dass Meta-Funktionen 
sind, dann schaue ich halt nach. Das ist eine Holschuld: ich muss das 
keinem hier beibringen.
Das Vorurteil wird gerne gefüttert - und zwar solange, wie jemand nicht 
bereit ist, sich auf neue Dinge einzulassen. Leider passiert das hier 
aber regelmäßig, es werden nur die altbekannten Statements ausgetauscht. 
Und darüber staune ich, sollte es doch in einem technologiegeprägtem 
Metier eigentlich anders zugehen.

> Carl D. schrieb:
>> Wobei man durch McuCpp leicht erschreckt werden kann, denn es basiert
>> auf C++03, wo man vieles was in C++17 als einfacher Code daherkommt,
>> kompliziert als Meta-Programm schreiben muß. Ich nenne nur mal
>> "parameter packs" als Sprachkonstrukt vs "loki"-Template-Halden.
>> Von dem, was der Benutzer der Bibliothek hinschreibt, ist das aber fast
>> auf aktuellem Stand.
>
> Siehe oben:
> Bitte an compilierbaren Beispielen detaillierter erklären. Wenn möglich
> thematisch separiert, jeweils ein c++03 snippet vs. aequivalentes c++17
> snippet.

Noch Wünsche bitte?

von Wilhelm M. (wimalopaan)


Lesenswert?

Wilhelm M. schrieb:

Sehe gerade einen Fehler:
>
1
> 
2
> led1 = Pin<PortA, 1>;
3
> led2 = Pin<PortB, 2>;
4
> led3 = Pin<PortC, 3>;
5
> 
6
> using ledController = MAX1234<led1, led2, led3>;
7
> 
8
> ledController::doSomething();
9
>

Das sollte natürlich heissen:
>
1
> 
2
> using led1 = Pin<PortA, 1>;
3
> using led2 = Pin<PortB, 2>;
4
> using led3 = Pin<PortC, 3>;
5
> 
6
> using ledController = MAX1234<led1, led2, led3>;
7
> 
8
> ledController::doSomething();
9
>

von Vincent H. (vinci)


Lesenswert?

Der Spaß geht ohne variadic templates und damit C++11 nicht. Oder 
zumindest wärs den Aufwand imho nicht Wert.

von Carl D. (jcw2)


Lesenswert?

Vincent H. schrieb:
> Der Spaß geht ohne variadic templates und damit C++11 nicht.

Doch, das hatte ich ja geschrieben, es geht mit Hilfe der 
Meta-Programming-Lib "loki" von Andrei Allexandrescu ab C++03 (2003 ist 
das Entstehungsjahr von "loki"), aber wäre in C++17 einfacher zu machen 
und besser zu verstehen.

von Vincent H. (vinci)


Lesenswert?

Carl D. schrieb:
> Vincent H. schrieb:
>> Der Spaß geht ohne variadic templates und damit C++11 nicht.
>
> Doch, das hatte ich ja geschrieben, es geht mit Hilfe der
> Meta-Programming-Lib "loki" von Andrei Allexandrescu ab C++03 (2003 ist
> das Entstehungsjahr von "loki"), aber wäre in C++17 einfacher zu machen
> und besser zu verstehen.

Ja boost::mpl oder so emuliert ja auch variadic Verhalten... aber na 
danke, sicher nicht freiwillig. =)

von Wilhelm M. (wimalopaan)


Lesenswert?

Nee, lasst die ollen Kamellen da, wo sie hingehören!

von Christian K. (mick-roc)


Lesenswert?

Ich denke meine ursprüngliche Frage ist beantwortet, ich fasse für mich 
zusammen: Ja, CRTP bedeutet, dass der Nutzer eines Interfaces ebenfalls 
ein Template sein muss, siehe

Dr. Sommer schrieb:
> Bei statischer Verknüpfung zwischen interface und dessen
> Nutzer müssen halt alle Nutzer es statisch kennen und daher ggf.
> Templates werden. Das ist der Preis wenn man kein "virtual" will...

Praktikable Techniken um diesen Umstand zu umgehen habe ich bisher nicht 
gelesen, sodass ich akzeptiere, dass es sie vermutlich nicht gibt.

Wilhelm M. schrieb:
> zu 1) mixin from above: war m.E. noch nie eine sooo tolle Idee.
>
> zu 2) stat. Polymorphie "Helper": nur eine "historische" Krücke, deren
> Einsatz m.E. überschätz wurde. Die richtige Lösung heisst concepts.

Diese Einschätzung erscheint mir logisch. Ich denke, dass CRTP eine 
interessante Technik ist, die man kenne sollte. Für meine Anforderungen 
an ein polymorphes Interface scheint sie aber ungeeignet zu sein.


Zu der aufgekommenen Diskussion über Sinn- und Unsinn von C++ und 
abstrakten Programmiertechniken in der Embedded Software:
Eine zusätzliche Einschränkung, die bisher noch nicht angesprochen 
wurde, ist die Compiler-Verfügbarkeit. Der neuste Arm Compiler, Version 
6, wirbt damit C++14 zu unterstützen. Ich arbeite immer noch viel mit 
Version 5, die C++03 vollständig und C++11 teilweise beherrscht... von 
C++17 oder gar C++20 ist die Embedded Software vermutlich noch weit 
entfernt. Ich beschränke mich derzeit auf C++98/C++03 als kleinsten 
gemeinsamen Nenner für alle Plattformen.

Variadic templates, Concepts, u.ä. stehen für mich daher leider nicht 
zur Debatte. Ich suche nach einem Spagat zwischen moderner (mMn 
effizienterer) Technik und konservativer Entwicklung, was scheinbar der 
Suche nach dem heiligen Gral entspricht...

Carl D. schrieb:
> Doch, das hatte ich ja geschrieben, es geht mit Hilfe der
> Meta-Programming-Lib "loki" von Andrei Allexandrescu ab C++03

Das ist ein sehr interessanter Hinweis, mit der Bibliothek und seinem 
Buch werde ich mich eingehender befassen, danke!

von Wilhelm M. (wimalopaan)


Lesenswert?

Christian K. schrieb:
> Wilhelm M. schrieb:
>> zu 1) mixin from above: war m.E. noch nie eine sooo tolle Idee.
>>
>> zu 2) stat. Polymorphie "Helper": nur eine "historische" Krücke, deren
>> Einsatz m.E. überschätz wurde. Die richtige Lösung heisst concepts.
>
> Diese Einschätzung erscheint mir logisch. Ich denke, dass CRTP eine
> interessante Technik ist, die man kenne sollte. Für meine Anforderungen
> an ein polymorphes Interface scheint sie aber ungeeignet zu sein.

Denke ich auch: Du kannst Dein Problem ganz ohne CRTP lösen 
(Konsequenzen: s.o.)

> Eine zusätzliche Einschränkung, die bisher noch nicht angesprochen
> wurde, ist die Compiler-Verfügbarkeit. Der neuste Arm Compiler, Version
> 6, wirbt damit C++14 zu unterstützen.

Welcher Compiler ist es denn konkret? Es gibt ja mehr als einen ...

> Variadic templates, Concepts, u.ä. stehen für mich daher leider nicht
> zur Debatte. Ich suche nach einem Spagat zwischen moderner (mMn
> effizienterer) Technik und konservativer Entwicklung, was scheinbar der
> Suche nach dem heiligen Gral entspricht...

Dann schau Dir auf jeden Fall constexpr in allen seinen Ausprägungen an 
(constexpr-functions, constexpr members, literal types, constexpr 
lambdas).

> Carl D. schrieb:
>> Doch, das hatte ich ja geschrieben, es geht mit Hilfe der
>> Meta-Programming-Lib "loki" von Andrei Allexandrescu ab C++03
>
> Das ist ein sehr interessanter Hinweis, mit der Bibliothek und seinem
> Buch werde ich mich eingehender befassen, danke!

Würde ich nicht machen. Ist definitiv Schnee von gestern. Investiere 
Deine Zeit in Maßnahmen, wie Du Deine Code- und Tool-Basis modernisieren 
kannst.

von Dr. Sommer (Gast)


Lesenswert?

Christian K. schrieb:
> Der neuste Arm Compiler, Version 6, wirbt damit C++14 zu unterstützen.

Ja, weil er auf Clang basiert... ;-) der freie GCC unterstützt auch 
C++17 für ARM. Im Atollic Studio z.B. gibt es da auch eine nette IDE 
für.

von Jan K. (jan_k)


Lesenswert?

Wilhelm M. schrieb:
> Welcher Compiler ist es denn konkret? Es gibt ja mehr als einen ...

Er meint den von ARM Keil Mdk mitgelieferten. Version 5 hieß armcc, 
Version 6 armclang, der wie oben erwähnt auf clang basiert. Damit 
funktionieren auch sehr viele Compiler flags vom gcc. Und er kann lto. 
Und c++14 bzw sogar 17 als community Version, d.h. Noch nicht offiziell 
unterstützt.

von Johann (Gast)


Lesenswert?

Ich hatte in unserer Firma dank Chipmangel ein sehr komplexes Projekt 
von einer MCU auf die andere zu Portieren. Dank Abstraktion habe ich das 
in einem Tag (samt kurzem Systemtest) geschafft. Hätte ich die 
effizienteste Implementierung gewählt, tüftelte ich noch Tage an der 
Separierung von MCU spezifischem Code und Applikation.

Außerdem ist es nett zu sehen, wie eine ganze Library an IC Treibern 
automatisch funktioniert, sobald ich für eine neue MCU SPI und I2C 
implementiere.

von Jasson J. (jasson)


Lesenswert?

Tom schrieb:
> Peter D. schrieb:
>> riesen Wust an Code man in C++ für die
>> allerkleinsten Aufgaben
>
> Ein Problem, das so komplex ist, dass abstraktere Programmiertechniken
> am Ende einfacher (und weniger fehleranfällig sind) sind, eignet sich
> schlecht für ein Mini-Beispiel, das zum Verstehen und im Forum fragen
> geeignet ist.
>
> Die Diskusion läuft hier immer ungefähr so ab:
> A: "Ich habe Technik XY entdeckt, mit der man nach 2h Einarbeitung
> riesige Probleme flexibel und fehlersicher bearbeiten kann. Hier ein
> Miniatur-Beispiel anhand eines LED-Blinkers, wie genau könnte man das am
> besten auf das Feature UVW erweitern?"
>
> B: "Du idiot, led blinker sind in retro-C viel einfacher LOL."
>
> C: "Das ist alles viel zu langsam, in Assembler läuft die Blinkschleife
> mit 2 Takten weniger."

Oder so, dass man entweder sein ganzes Projekt postet womit man die 
Antwort erntet "Wir entwickeln dir doch nicht dein Projekt"
oder
man kreist sein Problem ein und postet nur den Teil oder etwas 
repräsentatives - dann kommt "Ich kann nur aus der Glaskugel den Rest 
deines Codes erahnen - poste doch mal alles"

Und dann wird sich gewundert, dass man so passiv-aggresive / arrogante 
"Hilfe" nicht haben möchte

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.