Hallo,
habe folgendes Testmakro
#define A 1
#define Test(x) x
Aufruf: Test(A)
Ergebnis : A
Ich hätte aber gerne das 1 rauskommt. Hat jemand eine Idee wie ich das
Makro änder muss, damit ich 1 erhalte?
AVRJohnny schrieb:> warum ist das so?
Ganz bis zu Ende verstanden habe ich das auch nie, um ehrlich zu sein
;),
mir aber auch nie Mühe gegeben. Die entsprechenden Beschreibungen im
Standard (Abschnitt 6.10.3) sind nicht so ganz trivial zu begreifen.
Jörg W. schrieb:> AVRJohnny schrieb:>> warum ist das so?>> Ganz bis zu Ende verstanden habe ich das auch nie, um ehrlich zu sein> ;),
Dito.
Ich glaub das hat bis auf Ritchie noch nie irgendjemand komplett
begriffen.
Das ist eines der grossen Mysterien des Präprozessors. Was mich nur
wundert ist, wie die Compilerhersteller es immer wieder schaffen, genau
diesen Effekt zu reproduzieren :-)
Karl H. schrieb:> Ich glaub das hat bis auf Ritchie noch nie irgendjemand komplett> begriffen.
Und der ist mittlerweile tot …
> Das ist eines der grossen Mysterien des Präprozessors. Was mich nur> wundert ist, wie die Compilerhersteller es immer wieder schaffen, genau> diesen Effekt zu reproduzieren :-)
Vielleicht haben sie ja alle seinen Quellcode dafür bekommen. :)
Karl H. schrieb:> Das ist eines der grossen Mysterien des Präprozessors. Was mich nur> wundert ist, wie die Compilerhersteller es immer wieder schaffen, genau> diesen Effekt zu reproduzieren :-)
Schaffen sie auch nicht. Ich bin vor Jahren im Umfeld von µCs einem
Compiler begegnet, der bei solchen Fiesemantenten scheiterte.
> Ich glaub das hat bis auf Ritchie noch nie irgendjemand komplett> begriffen.
Kunststück. Ritche hat halt mal eben einen Präprotz geschrieben und dann
dessen zufällig entstandenes Verhalten zum Standard erkoren.
A. K. schrieb:> Ritche hat halt mal eben einen Präprotz geschrieben und dann dessen> zufällig entstandenes Verhalten zum Standard erkoren.
Wobei die rekursive Ersetzung ja schon ein nettes Feature ist, das
man hin und wieder mal ganz gut gebrauchen kann. Immerhin schafft
man es damit (zusammen mit dem token pasting), sowas wie
1
#define LED1 B, 4
2
#define LED2 A, 5
in die passenden Befehle umrubeln zu lassen, mit denen man dann
entsprechend bspw. auf einem AVR auf DDRx und PORTx zugreift.
Zwischenmacros benötige ich, um:
1. die Argumentprüfung zu umgehen.
2. den ## Operator zu verwenden.
Nur im ersten Durchlauf überprüft der Präprozessor die Anzahl der
Argumente bzw. verbindet 2 Ausdrücke ohne vorherige Expansion.
Getestet mit dem gcc. Das erste gibt 1 aus, das zweite gibt A aus. Ob
die Parameter eines Makros nochmal expandiert werden sollen wird mit dem
'#' vor dem Parameternamen (bei dessen Verwendung) gesteuert.
Jörg W. schrieb:> Wobei die rekursive Ersetzung ja schon ein nettes Feature ist, das> man hin und wieder mal ganz gut gebrauchen kann. Immerhin schafft> man es damit (zusammen mit dem token pasting), sowas wie> #define LED1 B, 4> #define LED2 A, 5>> in die passenden Befehle umrubeln zu lassen, mit denen man dann> entsprechend bspw. auf einem AVR auf DDRx und PORTx zugreift.
Ja das ist schon schön. Nur die Makros dahinter sind nicht so schön ;)
Jens D. schrieb:> wie greife ich denn auf den Port und die Nummer zu?
Für LEDs habe ich gerade nichts zur Hand, aber hier das, was ich in
meinem HD44780-Treiber benutze.
project.h definiert, wo das LCD angeschlossen ist:
1
/* HD44780 LCD port connections */
2
#define HD44780_RS C, 6
3
#define HD44780_RW C, 1
4
#define HD44780_E C, 0
5
/* The data bits have to be in ascending order. */
6
#define HD44780_D4 D, 2
hd44780.c hat dann:
1
#define GLUE(a, b) a##b
2
3
/* single-bit macros, used for control bits */
4
#define SET_(what, p, m) GLUE(what, p) |= (1 << (m))
5
#define CLR_(what, p, m) GLUE(what, p) &= ~(1 << (m))
A. K. schrieb:> Kunststück. Ritche hat halt mal eben einen Präprotz geschrieben und dann> dessen zufällig entstandenes Verhalten zum Standard erkoren.
Nein, das ist kein Zufallsprodukt. Dieses Verhalten hat auch der
m4-Macroprozessor. Das ermöglicht eine ganze Menge Tricks, ist aber
etwas unübersichtlich - nur das gilt für den ganzen m4...
So läuft die Macroexpansion ab:
1. Zuerst werden die aktuellen Parameter in der Definition substituiert.
Dabei werden sie nicht evaluiert.
2. Dann wird die rechte Seite nach weiteren Macros durchsucht. Wenn
dabei Macros mit Parametern gefunden werden, werden diese Parameter
zuerst evaluiert und dann folgt die Expansion.
Jörg W. schrieb:> Walter T. schrieb:>> Dafür nutze ich mittlerweile inline-Funktionen.>> Und wie machst du in einer Inline-Funktion aus einem> B, 4> dann ein> PORTB |= 1 << (4);> ?
OK, jetzt verstehe ich den Sinn dieser Aktion erst. Du willst den Port
in einer Art "Initialisierung" festlegen, nicht im Header.
Nein, soetwas mache ich tatsächlich nicht. Bei mir sähe das so aus:
mec schrieb:>> #define LED1 B, 4>> #define LED2 A, 5>>>> in die passenden Befehle umrubeln zu lassen, mit denen man dann>> entsprechend bspw. auf einem AVR auf DDRx und PORTx zugreift.>> Ja das ist schon schön. Nur die Makros dahinter sind nicht so schön ;)
Ach, so etwas findest du schön?
Ich nicht.
Das simple Ersetzen von schnöden Zahlen durch lesbare Bezeichner ist
völlig in Ordnung, also so etwas
#define joerg 0
#define karl 1
und so weiter.
Aber daraus irgendwelche Kunst-Ungetüme zu machen, ist die
personifizierte Abscheulichkeit, die im übrigen kein Mensch auf dieser
Welt wirklich braucht. Wer da meint, er bräuchte ein Makro, um
PortX,PinY architekturunabhängig zu setzen, hat einfach keinen Überblick
darüber, an welcher Stelle man in seiner Firmware die Hardware
abstrahieren sollte.
Jörg W. schrieb:> Benutzt werden sie so:
Ja, da hast du ein abschreckendes Beispiel aufgezeigt. Eigentlich geht
es darin nur um das simple Ausgeben eines Nibbles und nachfolgenden
Hi-Puls an Enable. Aber es ist aufgebauscht ohne Ende - und im Grunde
ohne jeglichen Sinn.
W.S.
W.S. schrieb:> Ach, so etwas findest du schön?> Ich nicht.
De gustibus non disputandum.
> Aber daraus irgendwelche Kunst-Ungetüme zu machen, ist die> personifizierte Abscheulichkeit, die im übrigen kein Mensch auf dieser> Welt wirklich braucht.
Doch. Es geht darum Informationen klar und deutlich zu vermitteln und
gleichzeitig mögliche Fehlerquellen auszuschliessen. Wenn das mit ein
paar vielleicht wild aussehende Makros machbar ist, gibt es keinen Grund
(außer MISRA ;-)) das nicht zu tun.
Pet schrieb:> Hallo,> habe folgendes Testmakro> #define A 1> #define Test(x) x>> Aufruf: Test(A)> Ergebnis : A> Ich hätte aber gerne das 1 rauskommt. Hat jemand eine Idee wie ich das> Makro änder muss, damit ich 1 erhalte?
Da kommt 1 raus.
Beweis:
Eric B. schrieb:> Doch. Es geht darum Informationen klar und deutlich zu vermitteln und> gleichzeitig mögliche Fehlerquellen auszuschliessen.
Und genau deshalb würdest du dich auf sowas fehlerträchtiges und
undurchsichtiges wie Makros verlegen? Du hast komische Ansichten.
Ich hab diesen Mist seit langem durch. Regelmäßig sieht man bei
Programmen, wo extensiv mit Makros gearbeiter wird eine ebenso extensive
Verwendung von void Pointern, ebensolchen Argumenten bei Funktionen und
deren Resultaten und Typecasts an fast jeder Stelle. Es ist wie ein
Dickicht aus Unkraut. Wie jemand da auf "Informationen klar und deutlich
zu vermitteln" kommt, ist unverständlich. Eher sehe ich dort den Drang
nach Verallgemeinerung per Makro um der Verallgemeinerung willen.
Jörg W. schrieb:> Und wie machst du in einer Inline-Funktion aus einem> B, 4> dann ein> PORTB |= 1 << (4);> ?
Eben, ein Makro zu schreiben, das aus einem SetPort(b,4) sowas macht wie
PortB |= 1<<4 trifft ja nur auf solche Architekturen zu, bei denen es
genau so gemacht werden muß. Schon der allerkleinste Blick über den
Tellerrand zeigt einem, daß es woanders ganz anders gemacht werden muß
und folglich so ein Makro eine prächtige Fehlerquelle ist. Portiere das
mal auf einen LPC oder STM32, dann siehst du was ich meine.
Deshalb: Man macht sowas besser ÜBERHAUPT NICHT.
Stattdessen abstrahiert man die Sache ein kleines Stück weiter oben,
also nicht das Setzen von Portpins abstrahieren wollen, sondern
Funktionen schreiben, die echte Zielfunktionen ausführen. Beispiel:
void Motor_Ein (void) { GPIOB_PSOR = 1<<4; }
Sowas kommt ohne irgendwelche Makros aus, ist leicht lesbar und ebenso
leicht an andere HW anpaßbar. Der Inhalt so einer Funktion ist mit 1
Anweisung ja durchaus übersichtlich - und wer will, deklariert diese
Funktion als inline.
W.S.
W.S. schrieb:> Und genau deshalb würdest du dich auf sowas fehlerträchtiges und> undurchsichtiges wie Makros verlegen? Du hast komische Ansichten.
Au Mann, das sind doch alles unverdaut nachgeplapperte Dogmen... Selber
denken macht schlau, nicht Gehorsamsleistungen und auf Punkte im
Programmiererhimmel ist gepfiffen.
C-Macros bieten - obwohl sie für Macros ziemlich doof sind - einige
Möglichkeiten, die man in C anders nicht bekommt. Und ja, man kann
Unsinn damit machen - aber das kann man so ziemlich mit jedem Werkzeug
und vor allem, wenn man nicht weiß, was man tut...
W.S. schrieb:> Deshalb: Man macht sowas besser ÜBERHAUPT NICHT.
Wir sind hier im Mikrocontroller Forum, also geht es meist auch um sehr
eingeschränkte Resourcen, und meist völlig andere Anforderungen, als im
normalen Programieralltag. Da können Macros und Compiler
Buildin-Funktionen und anderer "spezieler Kram" schon sehr praktisch
sein, natürlich muss man noch einige Abstraktionsschichten einbauen,
wenn man Programmteile Portabel halten will. Man braucht beides, bzw.
ihr habt beide recht.
Das mit
#define INFO_LED A,2
... weiterer Macrozauber
ist zB. gut dafür, um nur an einer Stelle die Verknüpfung der LED mit
dem Pin im Code zum haben, und den MCU-Eigenheiten von der Programmlogik
zu trennen. Sonst müsste man bei einem Pinwechsel z.B. in jeder
Funktion, welche die LED anspricht
wie z.B. set_info_led(){... SET_PIN(INFO_LED) /* nach belieben Macro
oder Funktion */ ... } einzeln anpassen.
hab wenig Zeit, hoffe es ist ein wenig verständlich ;)
mec schrieb:> Das mit> #define INFO_LED A,2> ... weiterer Macrozauber> ist zB. gut dafür, um nur an einer Stelle die Verknüpfung der LED mit> dem Pin im Code zum haben,
Nein, es ist eben NICHT gut.
Das Ziel der Sache ist es ja wohl, die Info-LED ein oder aus zu
schalten. Ob man dazu einen Portpin oder ein grünes Marsmännchen
braucht, ist dem übergeordneten Programm schnurz (bzw. sollte ihm
schnurz sein). Also setzt man die Abstraktion nicht bei der Zuordnung zu
einem Portpin an, sondern auf der darüberliegenden logischen Ebene, die
damit von der konkreten Hardware abgehoben funktioniert. Etwa so:
inline void InfoLed_ein (void) { .... }
und
inline void InfoLed_aus (void) { .... }
Das ist echte Hardware-Abstraktion und sie ist leserlicher und sauberer
und besser wartbar und sinnvoller als alle Versuche a la "#define
INFO_LED A,2".
Und nochwas:
Makros reduzieren NIEMALS den tatsächlichen Ressourcen-Aufwand, sondern
blähen ihn in vielen Fällen auf, da man damit Code mehrfach erzeugt, der
in vielen Fällen besser in einem separaten Unterprogramm aufgehoben
wäre, so daß er mehrfach genutzt werden kann. Deshalb ist das Folgende
einfach falsch gedacht:
mec schrieb:> Wir sind hier im Mikrocontroller Forum, also geht es meist auch um sehr> eingeschränkte Resourcen,...
W.S.
W.S. schrieb:> Das ist echte Hardware-Abstraktion und sie ist leserlicher und sauberer> und besser wartbar und sinnvoller als alle Versuche a la "#define> INFO_LED A,2".
Vollkommen richtig. Das macht aber solche Makros nicht unnütz.
Solche Makros sind ein Layer von vielen. Es ergibt nur beschränkt Sinn,
sie über die Grenzen von verschiedenen Mikrocontrollern anzuwenden, da
die Portstrukturen zu verschieden sind. Aber zur Konfiguration von Code,
den man zwischen verschiedenen Implementierungen übernimmt, können sie
schon einen Sinn ergeben.
Wenn man also Code für Text-LCDs so gestaltet, dass man nur ein paar
Makros anpassen muss, um ihn von vom Port B eines ATmega8 auf den Port A
eines ATmega644 zu verschieben, dann kann das durchaus sinnvoll sein.
Nur bleibt das innerhalb dieses LCD-Codes und seiner Konfiguration
verborgen, der Rest des Programms nutzt dessen Funktionen.
Uhu U. schrieb:> Au Mann, das sind doch alles unverdaut nachgeplapperte Dogmen... Selber> denken macht schlau, nicht Gehorsamsleistungen und auf Punkte im> Programmiererhimmel ist gepfiffen.
So sollte man das nicht sehen. Eine Programmiersprache ist letztlich
eine textuelle Abstraktion des Gedankengangs des Programmierenden.
Sprich: sie hat also auch die nicht-funktionale Aufgabe, die
Kommunikation mit Reviewern oder anderen Dritten zu erleichtern (im
Zweifelsfall sogar zu sich selber nach n Jahren). Und wenn man
MISRA-C(++) schreiben darf/muss und typkorrekt eine übelste Kaskade von
Makros entflechtet, kann da der Spaß irgendwo aufhören.
> C-Macros bieten - obwohl sie für Macros ziemlich doof sind - einige> Möglichkeiten, die man in C anders nicht bekommt.
Soweit gehe ich gerne mit - als Sprachfeature zur Optimierung in C bzw.
C99 sehe ich das gerne ein. Auch als Hilfsmittel zur quasi
"syntaktischen Erweiterung" der Sprache kann es sehr nützlich sein und
sogar auch gute Dinge ermöglichen (z.B. Unit-Test-Suites). Das aber nur,
wenn ein hartes Constraint auf die Verwendung der Programmiersprache C
liegt.
Intuitiv würde ich aber eher dazu neigen, für solche Kaskaden wie oben
eher C++ und templates zu nutzen. Das ist wenigstens halbwegs typsicher,
ist aber zugegeben bei falscher oder exzessiver Verwendung auch nicht
unbedingt besser zu lesen (Stichwort variadic templates C++11).
Vielleicht wäre vielleicht sogar die allerbeste Lösung noch, den zu
parametrisierenden Code als einzelnes plattformabhängiges Modul
dazuzulinken. Ob das dann mit C, C++ oder gar Asm erzeugt wird und wie
darin geschweinst wird, ist dann fast egal. Solange der Rest der Library
bzw. Binary sauber bleibt.
> Und ja, man kann> Unsinn damit machen - aber das kann man so ziemlich mit jedem Werkzeug> und vor allem, wenn man nicht weiß, was man tut...
Es ist halt was Wahres dran:
Ideal ist es, Code zu schreiben, den jemand so mühelos lesen kann, dass
derjenige den Eindruck bekommt, der Code wäre trivial.
W.S. schrieb:> Nein, es ist eben NICHT gut.
Ich mag es einfach zu schreiben und gut lesbar:
1
// hardware.h:
2
#include"sbit.h"
3
#define LED_GREEN PORT_B7
4
#define LED_GREEN_oe DDR_B7
5
6
// init.h:
7
LED_GREEN_oe=1;
8
9
// code:
10
LED_GREEN=1;
11
LED_GREEN=0;
W.S. schrieb:> Ob man dazu einen Portpin oder ein grünes Marsmännchen> braucht, ist dem übergeordneten Programm schnurz
Mir doch auch.
Ich definiere dazu virtuelle Ports, die z.B. zyklisch per SPI auf nen
74HC595 ausgegeben werden oder vom 74HC165 eingelesen:
Peter D. schrieb:> Ich mag es einfach zu schreiben und gut lesbar:
Ich mag Code, der potentiell keine Seiteneffekte hat. Und das kann man
bei Makro-Definitionen nicht durchgängig ausschließen. Da stimme ich
W.S. 100%ig bei der Bildung von HALs und der Minimierung von
Makro-Verwendungen zu.
>
1
>// hardware.h:
2
>#include"sbit.h"
3
>#defineLED_GREENPORT_B7
4
>#defineLED_GREEN_oeDDR_B7
5
>
6
>// init.h:
7
>LED_GREEN_oe=1;
8
>
9
>// code:
10
>LED_GREEN=1;
11
>LED_GREEN=0;
12
>
Für einfache Sachen bzw. extremste Optimierungen (AtTiny-Niveau) geht
das sicher in Ordnung. Für alles andere lieber einen gescheiten
Datenfluss im Programm haben, Kontrollfluss minimieren und hardwarenahe
Zugriffe in 'ne HAL packen. Die HAL kann ja dann wie oben geschrieben,
als einzelnes .o dazugelinkt werden, deren externe "C"-Exporte nur
innerhalb der .o hardwaregekoppelt sind.
Kann man dann auch mal schön in eine CMake-Umgebung reinstricken, die
dann die Builds automatisiert für alle möglichen Targets generieren kann
und sogar mit Testcases in 'nem Simulator bzw. QEmu abdecken könnte.
CMake muss dann nur für das jeweilige Target die richtige HAL dazulinken
zu lassen. Und linkt man da noch FlexeLint dazu, hat man sogar 'n
richtiges Framework für MISRA.
W.S. schrieb:> mec schrieb:>> Das mit>> #define INFO_LED A,2>> ... weiterer Macrozauber>> ist zB. gut dafür, um nur an einer Stelle die Verknüpfung der LED mit>> dem Pin im Code zum haben,>> Nein, es ist eben NICHT gut.
Doch ist es schon, ich will nicht den ganzen Code durchsuchen, um alle
Abhängikeiten von einen Pin zu finden und zu ändern, wenn ich nur eine
Pinzuordnung ändere. Ich ändere an einer Stelle meine Pinzuordnung, und
muss nicht alle Funktionen, welche den Pin verwenden, des HALs danach
abklappern
> Das Ziel der Sache ist es ja wohl, die Info-LED ein oder aus zu> schalten. Ob man dazu einen Portpin oder ein grünes Marsmännchen> braucht, ist dem übergeordneten Programm schnurz (bzw. sollte ihm> schnurz sein). Also setzt man die Abstraktion nicht bei der Zuordnung zu> einem Portpin an, sondern auf der darüberliegenden logischen Ebene, die> damit von der konkreten Hardware abgehoben funktioniert. Etwa so:>> inline void InfoLed_ein (void) { .... }> und> inline void InfoLed_aus (void) { .... }>> Das ist echte Hardware-Abstraktion und sie ist leserlicher und sauberer> und besser wartbar und sinnvoller als alle Versuche a la "#define> INFO_LED A,2".
Und du musst jede Funktion anpassen, wenn du mal eine Änderung bei der
Pinbelegung machst. Dann geht die LED an, aber nicht mehr aus, wenn du
was vergisst ;)
Was meinst du was das ist:
set_info_led(){... SET_PIN(INFO_LED) /* nach belieben Macro
oder Funktion */ ... }
> Makros reduzieren NIEMALS den tatsächlichen Ressourcen-Aufwand, sondern> blähen ihn in vielen Fällen auf, da man damit Code mehrfach erzeugt, der> in vielen Fällen besser in einem separaten Unterprogramm aufgehoben> wäre, so daß er mehrfach genutzt werden kann.
Das was du da schreibst, mache ich natürlich auch. Das was ich meine ist
eine Abstraktionsebene darunter einzufügen, was mir bei Hardware
abhängigen Code Tipparbeit und Fehlermöglichkeiten erspart und einen
einfacheren Wechsel der MCU ermöglicht, halt auf kosten einer weiteren
Abstraktionsschicht. Manches Dabei geht nur mit Macros, anderes auch als
Funktion. Da verwende ich auch Buildin-Funktionen des Compilers, dann
muss ich mich nicht mehr auf das Optimierungsvermögen des Compilers
verlassen, sondern weiß dann auch das der Bit-Toogle wirklich nur ein
Maschinenbefehl ist, ohne Nebenwirkungen. Und auf inline kannst du dich
auch nicht verlassen, wenn es der Compiler sogar anbieten würde. Kleine
4 bis 16 Bit energiespar MCUs im unteren MHz Berreich mit sehr
beschränkten Resourcen sind halt anders handzuhaben, als 32Bit
Rechenmonster, ohne großartige Echtzeitanforderungen ;)
Was du brauchst ist C++, da kannst du weitestgehend auf Makros
verzichten, aber C++ mit z.B. PIC16F ist irgendwie Overkill, und ein
32Bit ARM für eine einfache Batterieschaltung ebenfals ;)
Es gibt selten reines Schwarz und Weiß, meist ist die Welt Grau.
Falk S. schrieb:> Auch als Hilfsmittel zur quasi> "syntaktischen Erweiterung" der Sprache kann es sehr nützlich sein
Nein, als "syntaktische Erweiterung" der Sprache sind sie nicht
geeignet.
Macros sind in C dort angebracht, wo man unübersichtliche Operationen -
z.B. die Bitpfriemeleien bei der Bedienung von Ports - mit Namen
versehen und "übersichtlich machen" will.
Normalen Code sollte man eher sparsam damit "schmücken".
Ein Idiom, das ich gerne verwende, wenn immer wiederkehrende
Codefragmente - z.B. Fallunterscheidungen in einem switch - zu schreiben
sind, sieht so aus:
1
#define DETECT(x) case (x):
2
switch(v){
3
DETECT(a)machwas();break;
4
DETECT(a)machwasanderes();break;
5
...
6
...
7
}
8
#undef DETECT
Damit wird nicht das ganze Programm mit den Macros kontaminiert und es
bleibt übersichtlich.
Uhu U. schrieb:> #define DETECT(x) case (x):> switch (v) {> DETECT(a) machwas(); break;> DETECT(a) machwasanderes(); break;> ...> ...> }> #undef DETECT
Hä?
Wozu soll denn das gut sein? Das reduziert weder die Komplexität noch
spart es Schreibarbeit, das erzeugt einfach nur ein WTF??? beim Lesen
des Codes, da bleibt man dann dran hängen und beginnt völlig
unnötigerweise drüber nachzudenken welcher ausgeklügelte Mechanismus nun
das schon wieder sein soll und fragt sich 10 Sekunden lang ob man zu
blöd ist zu sehen was der tiefere Sinn davon sein soll, ob einem was
entgangen ist (man sucht ja immer erstmal die Schuld bei sich selbst
wenn man etwas nicht sofort versteht), solange bis man sieht dass
ABSOLUT KEIN Sinn dahinter steckt und das nur ein böser Witz zur
Code-Obfuskierung ist.
Wenn mir jemand diesen Code vererben würde, dreimal darfst Du raten was
ich gleich als erstes damit machen würde (unmittelbar folgend auf die
oben genannten 10 Sekunden, noch bevor ich irgendwas anderes damit mache
oder weiterlese).
Peter D. schrieb:> Ich definiere dazu virtuelle Ports, die z.B. zyklisch per SPI auf nen> 74HC595 ausgegeben werden oder vom 74HC165 eingelesen:#define LED_RED> SBIT( vpout0, 1 )>> volatile uint8_t vpout0;>> LED_RED = 1;
Und wo bleibt bei deinem Beispiel die Lesbarkeit? Und wo die eigentliche
Abstraktion?
Auf der Strecke bleibt sie.
Was muß ich mir unter "LED_RED = 1;" vorstellen, wenn ich mir nach einem
Jahr die Quelle wieder angucken muß? Ist es ein "LedRed_ein();" oder ein
"LedRed_aus();" ? Das hängt in der HW ja davon ab, gegen welches Rail
die LED geht, gelle?
Nein, so ein Stil ist mir viel zu wurschtelig und extrem
unübersichtlich. Anstatt eine les- und wartbare Abstraktion zu
schreiben, machst du dir hier nur eine unnütze Verkomplizierung. Da wäre
es einfacher lesbar, wenn du die direkten Portzugriffe hinschriebest,
denn die kennt man ja auswendig, sofern man das RefMan gelesen hat.
Naja - und die von dir erwähnten TTL-Chips deuten mir darauf hin, daß du
unter permanenter Portpin-Knappheit leidest - aber das ist ein anderes
Thema.
mec schrieb:> Doch ist es schon, ich will nicht den ganzen Code durchsuchen, um alle> Abhängikeiten von einen Pin zu finden und zu ändern, wenn ich nur eine> Pinzuordnung ändere.
Ich verstehe den Sinn deiner Ausführungen nicht. Was bittesehr soll "ich
will nicht den ganzen Code durchsuchen" bedeuten?
Bei meiner Art, die HW zu abstrahieren, muß ich nur an einer einzigen
Stelle - nämlich in der Quelle des zugehörigen Treibers - etwas ändern.
Alle anderen Programmteile greifen lediglich auf "InfoLed_ein()" bzw.
"InfoLed_aus()" zu und sind damit völlig unabhängig davon, welches Pin
dafür benutzt wird und wie konkret diese Birne anzusteuern ist. Könnte
ja auch sein, daß sie (wie bei Peter) an einem Pin eines externen
Schieberegisters liegt, was bedeuten würde, daß man im RAM einen
Pufferspeicher halten müßte für alle Bits dieses Schieberegisters und
nach Ändern eines Bits darin dessen Inhalt seriell herausschieben müßte.
Bei meinem "InfoLed_ein()" ist das treiberintern alles möglich, ohne daß
es das übergeordnete Programm interessiert. Bei deinem "#define INFO_LED
A,2" würde sowas überhaupt nicht gehen, sondern einen noch viel
unübersichtlicheren Makro erfordern. Merkst du jetzt endlich was?
Bei dem, was du schreibst, willst du eine Art theoretischen Port
erschaffen, mit dem du dann operieren willst. Aber so ein theoretischer
Port ist keine funktionelle Sache, hat also keine Abstraktion von
einem Stück Controller-Hardware hin zu einer Systemfunktionalität,
sondern will nur alle Portpins aller denkbaren Controller
generalisieren. Das ist das "SichImKreiseDrehen" auf derselben Ebene.
W.S.
Bernd K. schrieb:> Uhu U. schrieb:>>> #define DETECT(x) case (x):
...
> Wozu soll denn das gut sein? Das reduziert weder die Komplexität noch> spart es Schreibarbeit, das erzeugt einfach nur ein WTF??? beim Lesen> des Codes,http://i.imgur.com/J1svNp7.jpg
Ausserdem widerspricht es genau das, was Uhu selber davor noch schrieb:
> Nein, als "syntaktische Erweiterung" der Sprache sind sie nicht> geeignet.
W.S. schrieb:> mec schrieb:>> Doch ist es schon, ich will nicht den ganzen Code durchsuchen, um alle>> Abhängikeiten von einen Pin zu finden und zu ändern, wenn ich nur eine>> Pinzuordnung ändere.>> Ich verstehe den Sinn deiner Ausführungen nicht. Was bittesehr soll "ich> will nicht den ganzen Code durchsuchen" bedeuten?>> Bei meiner Art, die HW zu abstrahieren, muß ich nur an einer einzigen> Stelle - nämlich in der Quelle des zugehörigen Treibers - etwas ändern.> Alle anderen Programmteile greifen lediglich auf "InfoLed_ein()" bzw.> "InfoLed_aus()" zu und sind damit völlig unabhängig davon, welches Pin> dafür benutzt wird und wie konkret diese Birne anzusteuern ist. Könnte> ja auch sein, daß sie (wie bei Peter) an einem Pin eines externen> Schieberegisters liegt, was bedeuten würde, daß man im RAM einen> Pufferspeicher halten müßte für alle Bits dieses Schieberegisters und> nach Ändern eines Bits darin dessen Inhalt seriell herausschieben müßte.> Bei meinem "InfoLed_ein()" ist das treiberintern alles möglich, ohne daß> es das übergeordnete Programm interessiert. Bei deinem "#define INFO_LED> A,2" würde sowas überhaupt nicht gehen, sondern einen noch viel> unübersichtlicheren Makro erfordern. Merkst du jetzt endlich was?>> W.S.
Nochmal zum mitmeiseln, wenn du im folgenden Beispiel, nach deinem
Ansatz, den Pin 0 von Port A mit der Funktion FU1 ändern willst, musst
du in jeder Funktion eine Änderung durchführen.
void FU1_SetHigh(void) { _LATA0 = 1; }
void FU1_SetLow(void) { _LATA0 = 0; }
void FU1_Toggle(void) { _LATA0 ^= 1; }
void FU1_GetValue(void) { _RA0; }
void FU1_SetDigitalInput(void) { _TRISA0 = 1; }
oder min in drei def-Makros für die drei verschiedenen Register. Wenn
das nicht Fehleranfällig ist?
zur Info die _XXX Schreibweise ist nur eine Vereinfachung von Microchip
#define _LATA0 LATAbits.LATA0
Mein Ansatz, stark vereinfacht:
#include MEINE_MAKROS
#define FU1 A,0 // Ich muss nur hier eine änderung durchführen
void FU1_SetHigh(void) { SET_PIN(FU1) }
void FU1_SetLow(void) { CLEAR_PIN(FU1) }
void FU1_Toggle(void) { TOGGLE_PIN(FU1) }
void FU1_GetValue(void) { GET_PIN(FU1) }
void FU1_SetDigitalInput(void) { SET_TO_DIGI(FU1) }
Diese Makros wie "SET_PIN(FU1)" sind einfach noch eine
Abstraktionsebene, um die Hardware schnell anpassen zu können, und meine
ganzen Pin-Definitionen wie "#define FU1 A,0" sind auch zentral in einem
Header, da muss ich nur 2 Zeichen ändern, und nicht die Ganze Datei
durchsuchen, wie du, und wenn ich Pins tauschen will, reicht es 4
Zeichen in einer einzigen Datei zu ändern.
Natürlich geht dieser Ansatz nicht immer, bei einem Schieberegister
anstadt MCU-Pin würde ich dann vielleicht, je nach Aufwand, mir ein
weiteres Makro/Funktionenpaket dafür machen (nach möglichkeit
wiederverwendbar), oder es halt dierekt da machen wo du es auch machst.
Oder ich benutze nur meine Makros, so wie ich lust drauf habe ;)
Wie gesagt, die Welt ist meist Grau.
W.S. schrieb:> Das hängt in der HW ja davon ab, gegen welches Rail> die LED geht, gelle?
Ist auch kein unlösbares Problem. In Altium heißen bei mir low-aktive
Signale "/Irgendwas" und in C eben "xIrgendwas".
W.S. schrieb:> Naja - und die von dir erwähnten TTL-Chips deuten mir darauf hin, daß du> unter permanenter Portpin-Knappheit leidest - aber das ist ein anderes> Thema.
Nö, an den Portpins liegt es nicht (AT90CAN128).
Ich hab eben oft galvanisch isolierte Schaltungskreise (über ADuM1401).
Oder ich möchte größere Lasten schalten (TPIC6B595).
Oder ich möchte keine riesen Kabelbäume ziehen.
mec schrieb:> void FU1_SetHigh(void) { _LATA0 = 1; }
OK, du willst es nicht verstehen.
Also nochmal: ich will dediziert keine noch so vigilantischen Ausdrücke,
die sich auf das Setzen von Portpins beziehen, sondern Funktionen, die
was Tatsächliches bewirken.
Also NICHT:
void FU1_SetMyPortIrgendwie()
sondern SO:
void SchalteMotorEin()
void MacheTüreAuf()
bool IstNachbarZuhause()
W.S.
W.S. schrieb:> mec schrieb:>> void FU1_SetHigh(void) { _LATA0 = 1; }>> OK, du willst es nicht verstehen.>> Also nochmal: ich will dediziert keine noch so vigilantischen Ausdrücke,> die sich auf das Setzen von Portpins beziehen, sondern Funktionen, die> was Tatsächliches bewirken.>> Also NICHT:> void FU1_SetMyPortIrgendwie()>> sondern SO:> void SchalteMotorEin()> void MacheTüreAuf()> bool IstNachbarZuhause()>> W.S.
Und wer Programiert dir dann diese Funktionen, wenn du das nicht selbst
machst?
Ich schrieb ja, das es sehr vereinfacht ausgedrückt ist, nochmal für
dich ein wenig ausführlicher. Ich mache sowas wie:
void schalte_motor_ein( void )
{
... //irgendwelcher Code
READ_PIN(MOTOR_READY)
... //irgendwelcher Code
SET_PIN(MOTOR_ENABLE)
... //irgendwelcher Code
}
void schalte_motor_aus( void )
{
... //irgendwelcher Code
MACHNOCHWAS(MOTOR_IRGENDWAS)
CLEAR_PIN(MOTOR_ENABLE)
... //irgendwelcher Code
}
Auf diese Weise wird dann sogar sowas wie "void schalte_motor_ein( void
);" zum Teil Portabel, also die PCB-Hardware wird von der MCU-Hardware
getrennt.
Bernd K. schrieb:> fragt sich 10 Sekunden lang ob man zu> blöd ist zu sehen was der tiefere Sinn davon sein soll
Die Das Beispiel ist nur zur Illustration gedacht. Mit etwas Nachdenken
wirst auch du sinnvolle Anwendungsfälle finden ;-)
Leider kommen jetzt keine Antworten mehr von W.S. und gleichgesinnten.
Also muss ich mal direckt nachfragen, ob ihr in dem was ich beschrieben
habe auch Vorteile, oder doch eher mehr Nachteile sieht, bzw ich etwas
wichtiges übersehen habe?
Weißt du, am Ende musst du doch selbst wissen, wie du am besten
zurecht kommst. Jeder muss da irgendwie seinen Weg finden.
Die ursprüngliche Frage drehte sich ja um die Art und Weise der
Ersetzung, und das mit der doppelten Ersetzung zum Erzeugen von
Namen wie PORTB und DDRB war nur als Beispiel gedacht.
Meiner Meinung nach (und so machen wir das auch in größeren Projekten)
hat es sehr wohl Sinn, unterhalb der von W.S. genannten Schicht noch
eine Abstraktionsebene zu haben, damit man beim Wechsel auf eine
(geringfügig) andere Hardware nicht alles neu zimmern muss. In C
macht sich diese Ebene nun mal am besten mit Makros, denn dann ist
garantiert, dass es eine sehr dünne Abstraktionsschicht wird, die
keinen eigenen Overhead mitbringt.
In C++ könnte man beide durchaus auch in einer Klasse gemeinsam
erledigen. Ein Beispiel dafür hatte ich hier mal gepostet:
Beitrag "Re: Wie lange Assemblerprogrammierung IC ADC ?"
Jörg W. schrieb:> Weißt du, am Ende musst du doch selbst wissen, wie du am besten> zurecht kommst. Jeder muss da irgendwie seinen Weg finden.>
Ja das stimmt. Und es muss auch das, was einen nicht liegt aber auch
geht, toleriert werden.
> Meiner Meinung nach (und so machen wir das auch in größeren Projekten)> hat es sehr wohl Sinn, unterhalb der von W.S. genannten Schicht noch> eine Abstraktionsebene zu haben, damit man beim Wechsel auf eine> (geringfügig) andere Hardware nicht alles neu zimmern muss. In C> macht sich diese Ebene nun mal am besten mit Makros, denn dann ist> garantiert, dass es eine sehr dünne Abstraktionsschicht wird, die> keinen eigenen Overhead mitbringt.
schön zu hören, dann habe ich wahrscheinlich die richtigen Schlüße aus
meinen noch wenigen MCU-C-Programmen gezogen. :)
mec schrieb:> Jörg W. schrieb:>> Weißt du, am Ende musst du doch selbst wissen, wie du am besten>> zurecht kommst. Jeder muss da irgendwie seinen Weg finden.>>> Ja das stimmt. Und es muss auch das, was einen nicht liegt aber auch> geht, toleriert werden.
Toleriert wird es sowieso. Auch wenn jeder da seine eigene Meinung hat.
Die normative Kraft des Faktischen schlägt jede Theorie.
Letzten Endes legt man sich beispielsweise so einen Satz Makros einmal
in einem Header File an, verbannt das auf ein Common Directory (auf dem
man alles mögliche wiederverwendbare Zeugs sammelt) und includiert von
dort ohne sich jemals wieder grossartig viele Gedanken über die dahinter
steckende Makro-Magie zu machen. Man verwendet dann nur noch das Zeugs
in weiterer Folge nachdem man sich einmalig da durchgekämpft und sich
für eine Variante entschieden hat.
Karl H. schrieb:> und includiert von> dort ohne sich jemals wieder grossartig viele Gedanken über die dahinter> steckende Makro-Magie zu machen.
..und fällt dann bei relativ geringfügigen Hardwareunterschieden
grandios auf die Nase, weil selbige nicht im Mindesten in einem alten
Makro berücksichtig wurden - wie auch?
Also: Makros, die garantiert hardwareunabhängig sind, kann man ja so
behandeln, aber hier haben wir es die ganze Zeit mit dem Versuch zu tun,
in Makros Hardwaredetails zu behandeln, wie eben das Setzen von Portpins
und so. Das war schon immer, ist und wird bis ewig MUMPITZ sein, der
einem auf lange Sicht mehr Scherereien macht als nützt. Wer es trotzdem
will und Probleme gern hat und nicht hören will, der möge das tun -
bittesehr.
Seit dem hartnäckigen NichtBegreifenWollen von mec halte ich mich da
einfach raus, macht und meint was ihr wollt - Hauptsache in meinem
nächsten Auto ist möglichst KEIN Code von euch drin.
W.S.
W.S. schrieb:> Also: Makros, die garantiert hardwareunabhängig sind, kann man ja so> behandeln, aber hier haben wir es die ganze Zeit mit dem Versuch zu tun,> in Makros Hardwaredetails zu behandeln, wie eben das Setzen von Portpins> und so. Das war schon immer, ist und wird bis ewig MUMPITZ sein, der> einem auf lange Sicht mehr Scherereien macht als nützt. Wer es trotzdem> will und Probleme gern hat und nicht hören will, der möge das tun -> bittesehr.>> Seit dem hartnäckigen NichtBegreifenWollen von mec halte ich mich da> einfach raus, macht und meint was ihr wollt - Hauptsache in meinem> nächsten Auto ist möglichst KEIN Code von euch drin.
Wie löst du so eine Aufgabe:
Man hat eine Hardware FU1, welche mit folgenden Funktionen angesprochen
wird.
Normalerweise greifen die Funktionen auf die gleichen Pins und
Register/RAM_Bereiche zu.
Dann muß die Pinzuordnung oder ein Register/RAM-Bereich geändert werden.
Änderst du dann in jeder Funktion den Code, ist das nicht auch sehr
Fehleranfällig, oder gehst du ganz anders vor?
void FU1_on(void) { ... }
void FU1_off(void) { ... }
FU1_state FU1_GetState(void) { ... }
void FU1_State1(void) { ... }
void FU1_State2(void) { ... }
void FU1_ToggleState(void) { ... }
uint16_t FU1_GetValue(void) { ... }
int FU1_Write(uint16_t value) { ... }
...
Auch wenn ich nicht W.S. bin (und nie gedacht hätte, dass ich ihm mal
bei irgendwas zustimmen würde...):
Die Pins darf man natürlich immer noch hinter Makros verstecken.
fu.h
1
voidFU1_on(void);
fu.c
1
#define FU1_PIN 2
2
#define FU1_PORT PORTB
3
4
voidFU1_on(void)
5
{
6
FU1_PORT|=FU1_PIN;
7
}
main.c
1
#include"fu.h"
2
intmain(void)
3
{
4
while(1)
5
{
6
FU1_on();
7
}
8
}
Als alleinige Abstraktionsebene bringen Makros fast nichts und machen
den Code pseudoportabel.
1
#define FU1_PIN 2
2
#define FU1_PORT PORTB
3
#define FU1_ON FU1_PORTB |= FU1_PIN
4
5
intmain(void)
6
{
7
while(1)
8
{
9
FU1_ON;
10
}
11
}
Aber das Thema hatten wir schon 1000x mit dem gleichen Ergebnis:
Die Abstrahierer-Fraktion hält die Assembler/Makro-Ecke für nicht zu
höherem Denken fähig und die Assembler/Makro-Ecke hält die Abstrahierer
für weltfremde Elfenbeinturmarchitekten. Alle bringen
Mickeymaus-Beispiele mit LEDs, die -- wie man aus der Praxis weiß --
stets besonders ressourcensparend oder flexibel eingeschaltet werden
müssen, und am Ende halten sich alle weiterhin gegenseitig für Idioten.
Tom schrieb:> Alle bringen Mickeymaus-Beispiele mit LEDs, die -- wie man aus der> Praxis weiß -- stets besonders ressourcensparend oder flexibel> eingeschaltet werden müssen
Nun, mein Mickeymouse-Beispiel war immerhin für HD44780. :) Dort ist
es vor allem dafür da, damit man nicht bei jedem neuen Board durch den
Code durchlatschen muss. Dennoch war die Portierung des gleichen Codes
auf den SAMD2x kein großer Akt, wenngleich natürlich nicht 1:1 (DDR
heißt DIR, PORT heißt OUT, PIN heißt IN, _delay_ms heißt delay_ms –
kann man aber alles mit Suchen&Ersetzen erschlagen).
Ich habe das Ding ja auch nur zitiert, weil es einer der wenigen
Fälle ist, bei denen man die zweistufige Ersetzung des Präprozessors
mal gebrauchen kann.
Ansonsten, auch wenn du's nicht glaubst, für Debug-Pins (zum Tracen
auf LA) ist es uns auch im kommerziellen Umfeld extrem wichtig, dass
es so schnell wie nur möglich geht, denn das Pin-Gewackel soll das
Timing des Codes nicht durcheinander bringen. Da ist es dann schon
nervig, dass man für ein Pintoggle auf dem SAM4 doppelt so viel Zeit
braucht wie für Pinset/-clear, da man zweimal auf den lahmen IO-Bus
muss (erst Pinstatus einlesen, danach Setzen oder Löschen).
Tom schrieb:> Auch wenn ich nicht W.S. bin (und nie gedacht hätte, dass ich ihm mal> bei irgendwas zustimmen würde...):>> Die Pins darf man natürlich immer noch hinter Makros verstecken.
Darum gehts doch der Makro-Fraktion gar nicht nur.
Worum es der Makrofraktion geht, das ist, das hier zum Bleistift
>
1
>#defineFU1_PIN2
2
>#defineFU1_PORTPORTB
3
>
ein #define fehlt. Da fehlt noch ein
1
#define FU1_DDR DDRB
Das mag jetzt für viele (inklusive mir) kein Ding sein, diese 3 Angaben
auf gleich zu halten, da aber DDRB unmittelbar mit PORTB zusammenhängt,
wäre es nützlich, die Angabe 'B' da heraus zu extrahieren und getrennt
zu haben. Auf Deutsch: Der Compiler/Präprozessor soll sich selber
ausknobeln, dass wenn ich den Port B meine, das PORT-Register das PORTB
sein muss und das zugehörige DDR-Register das DDRB sein muss. Was man
erreichen möchte, das ist, dass dieser vom Programmierer einzuhaltende
Zusamennhang DDRB-PORTB vom Präprozessor erledigt wird. Muss ich mich
als Programmierer nicht darum kümmern, dann kann ich da auch keinen
Fehler machen.
Wie wichtig das für jemanden ist, muss er selbst entscheiden. Ich kann
gut ohne damit leben.
Schade ist es schon, daß ein Macro keine weiteren Macros definieren
kann.
Dann könnte man mit nur einem define Output, Input und Direction
erschlagen.
So muß ich für jeden Portpin bis zu 3 defines anlegen.
Peter D. schrieb:> So muß ich für jeden Portpin bis zu 3 defines anlegen.
Oder Dich heftig auf die Optimierungen des Compilers verlassen.
Oder Dich implizit auf bestimmte Adress-Offsets verlassen.
Einen Tod sterben wir eben immer :-)
Walter T. schrieb:> Einen Tod sterben wir eben immer :-)
Ach Walter, du hast hier einen Punkt ganz vergessen:
All diese grandiosen Gedankenspielchen von mec, Peter, Jörg und
KarlHeinz lassen IMMERZU eines außen vor, nämlich daß es sich bai all
diesen PortB Pin2 Spielchen entweder um einen theoretischen Port eines
theoretischen µC handelt oder eben immer wieder nur um den begrenzten
Horizont eines AVR.
Auf allen anderen Architekturen sehen die Port ganz anders aus und
müssen mit ganz anderen Strategien benutzt werden. Da sind derarige
Makros wie hier verhandelt komplett kontraproduktiv. Das Einzige was
wirklich hilft ist die Abstraktion auf funktionaler Ebene - was
logischermaßen verbunden ist mit dem Editieren des betreffenden
Treibers, wenn man ihn auf einer anderen Architektur haben will.
Ich kenne das zur Genüge. Tom hat es NOCHMAL klar herausgestellt:
Tom schrieb:> fu.h> void FU1_on(void);
Ja, eben. Im Hauptprogramm hat NICHTS zu stehen, was einen auf
irgendwelche Pins und so festnagelt, selbst fu.h muß frei von sowas sein
und in der Quelle fu.c stehen die Details, die eben hardware- und
projektabhängig sind - und die man als einzige editieren muß, wenn man
das ganze portieren will.
W.S.
W.S. schrieb:> Auf allen anderen Architekturen sehen die Port ganz anders aus und> müssen mit ganz anderen Strategien benutzt werden.
Sie sind einem dort aber auch nicht im Wege, wie ich dir bereits auf
den entsprechenden Einwurf am Anfang entgegnet habe.
Ja, wenn man nur mit dem begrenzten Horizont irgendeines ARMs die
Implementierung begonnen hätte, hätte man das nicht benötigt. Auch
nicht für Xmega.
Aber zurück zum Ursprung, es war nicht danach gefragt, wie man am
besten eine Hardwareabstraktion erledigt, sondern wie das mit diesen
verschachtelten Makros funktioniert (stand nicht explizit in der Frage,
war aber vom Fehlerbild her eigentlich klar). Wenn dir mein Beispiel
für
die Anwendung verschachtelter Makros nicht gefällt, steht es dir frei,
ein anderes zu posten. Alle Diskussionen darum, wie man die Hardware
am besten abstrahiert, gehen jedoch am Thema dieses Threads vorbei.
W.S. schrieb:> Das Einzige was> wirklich hilft ist die Abstraktion auf funktionaler Ebene
Ich denke nicht, dass das jetzt irgendwer bestritten hätte.
> - was> logischermaßen verbunden ist mit dem Editieren des betreffenden> Treibers, wenn man ihn auf einer anderen Architektur haben will.
Ja. Das schliesst aber immer noch nicht aus, dass ich auf einem AVR
gerne das Problem vermeiden möchte, wenn ich es kann, dass ich den
Port-Buchstaben an 2 Stellen warten muss.
Auf einer anderen Plattform hab ich andere mögliche Fallen, die anders
zu behandeln sind. Aber auf einem AVR ist es nun mal genau dieses.
Ob ich das dann nur ein einer Treiberebene einsetze oder nicht, macht
diesen Wunsch ja deswegen nicht obsolet.
W.S. schrieb:> All diese grandiosen Gedankenspielchen von mec, Peter, Jörg und> KarlHeinz lassen IMMERZU eines außen vor, nämlich daß es sich bai all> diesen PortB Pin2 Spielchen entweder um einen theoretischen Port eines> theoretischen µC handelt oder eben immer wieder nur um den begrenzten> Horizont eines AVR.
Ich kann dir versichern, das ich noch nie für einen AVR Programiert habe
und meine Beispiele auch nicht für einen AVR gedacht sind ;)
Außerdem wiederholst du dich ständig, und gehst überhaupt nicht auf die
Fragen ein, welche man dir stellt. In der zwischenzeit gehe ich davon
aus, dass du dich bei Änderungen im Code wirklich auf ein gutes "Suchen
und Ersetzen" der IDE verlässt. ;)
Außerdem denke ich, dass du es noch nicht verstanden hast, was ich/wir
wollen. Karl Heinz hat es verstanden, und wenn ich ihn richtig
interpretiere, geht er lieber deinen Weg, warum zählst du ihn dann zur
Makrofraktion ;)
von Karl Heinz:
"Das mag jetzt für viele (inklusive mir) kein Ding sein, diese 3 Angaben
auf gleich zu halten, da aber DDRB unmittelbar mit PORTB zusammenhängt,
wäre es nützlich, die Angabe 'B' da heraus zu extrahieren und getrennt
zu haben. Auf Deutsch: Der Compiler/Präprozessor soll sich selber
ausknobeln, dass wenn ich den Port B meine, das PORT-Register das PORTB
sein muss und das zugehörige DDR-Register das DDRB sein muss. Was man
erreichen möchte, das ist, dass dieser vom Programmierer einzuhaltende
Zusamennhang DDRB-PORTB vom Präprozessor erledigt wird. Muss ich mich
als Programmierer nicht darum kümmern, dann kann ich da auch keinen
Fehler machen.
Wie wichtig das für jemanden ist, muss er selbst entscheiden. Ich kann
gut ohne damit leben."
Peter D. schrieb:> Schade ist es schon, daß ein Macro keine weiteren Macros definieren> kann.> Dann könnte man mit nur einem define Output, Input und Direction> erschlagen.> So muß ich für jeden Portpin bis zu 3 defines anlegen.
Im IRMP mache ich es mit 2 defines bei AVR:
Zu konfigurieren in irmpconfig.h:
1
#define IRMP_PORT_LETTER B
2
#define IRMP_BIT_NUMBER 6
Und dann in irmp.h:
1
#define _CONCAT(a,b) a##b
2
#define CONCAT(a,b) _CONCAT(a,b)
3
#define IRMP_PORT CONCAT(PORT, IRMP_PORT_LETTER)
4
#define IRMP_DDR CONCAT(DDR, IRMP_PORT_LETTER)
5
#define IRMP_PIN CONCAT(PIN, IRMP_PORT_LETTER)
6
#define IRMP_BIT IRMP_BIT_NUMBER
Ähnliche (hardwareabhängige) Makros gibt es dann noch für XMega, STM32,
STM8, ESP8266, PIC und alle anderen µCs, wo IRMP drauf läuft.
Wenn es beim Preprocessor noch die Möglichkeiten gäbe, Werte nicht nur
zu konkatenieren, sondern sie auch aufzudröseln, könnte man einfach
schreiben:
W.S. schrieb:> Im Hauptprogramm hat NICHTS zu stehen, was einen auf> irgendwelche Pins und so festnagelt
Das ist Quatsch mit Soße, darum ging es hier doch gar nicht.
Ich mache das doch auch bei größeren Programmen, daß im Hauptprogramm
keinerlei Low-Level Zugriffe stehen. Z.B. die 6 Pins des HD44780 greift
auschließlich der LCD-Treiber zu und das Hauptprogramm ruft nur
LCD-Funktionen auf usw.
Man sollte immer Treiberschicht und Applikationsschicht soweit wie
möglich trennen. Aber das hat überhaupt nicht das geringste damit zu
tun, ob ich Pins als Macro definiere oder nicht.
W.S. schrieb:> Das Einzige was> wirklich hilft ist die Abstraktion auf funktionaler Ebene
Oder eben eine Zweischichtige Abstraktion. Dann sollte hier doch jeder
zufrieden sein.
Einmal wird auf unterster Ebene die konkrete Hardware
(=Pin-Setz-Mechanismus) abstrahiert, darüber die Funktion.
Das könnte dann z.B. so aussehen:
1
voidEnable_Led(void)
2
{
3
i2c_write(LED_CHANNEL)
4
pin_set(LED_ENABLE_CHANNEL)
5
}
Dann hat man auch gleich erschlagen wenn die LED an einem Port-Expander
hängt und man vielleicht noch explizit einen Ausgangstreiber einschalten
muss.
Die Low-Level-Driver für I2C und Port müssen an die Controllerhardware
angepasst werden, die Funktionale Abstraktion Enable_Led() muss ans
Board-Layout angepasst werden.
Die API der Low-Level-Driver ist im Idealfall immer gleich, unabhängig
vom Prozessor.
Dank LTO und Konsorten sollte auch kaum Overhead anfallen.
Lediglich Pin-Setzen mittels Funktion ist auf der konkreten Architektur
"AVR" immer bisl pfriemelig, wenn der Compiler nicht ersehen kann dass
sbi/cbi benutzt werden kann.
Frank M. schrieb:> Wenn es beim Preprocessor noch die Möglichkeiten gäbe, Werte nicht nur> zu konkatenieren, sondern sie auch aufzudröseln, könnte man einfach> schreiben:> #define MY_PORT_PIN B6
Yep, das wäre manchmal hübsch. Aber naja, mit
1
#define MY_PORT_PIN B,6
kann man durchaus auch leben.
Wobei, mit ein bisschen Mimik kann man sicher aus B6 ein "B6" machen,
und das wieder kann man mit "B6"[0] und "B6"[1] zugreifen. ;-)
Liest sich als herrsche hier keine klare Trennung zwischen
Portierbarkeit (auf eine andere Hardware) und Rekonfigurierbarkeit (z.B:
vergleichbare Hardware mit anderer Verdrahtung).
Die meisten Vorschläge haben offenbar nur eine einfache
Rekonfigurierbarkeit als Ziel. Makros zum Beispiel, die auf Bitfelder
abbilden, sind inhärent nicht-portabel. Auch Adresse(n) von Port(s) zu
übergeben ist nicht portabel da es bestimmte Implikationen macht, wie
entsprechende Port-SFRs zu bedienen sind.
Tja, Johann, vielleicht hast du ja noch ein schönes Beispiel, wo man
geschachtelte Makro-Ersetzungen sinnvoll brauchen kann. ;-)
Darum ging's ja in diese Thread, nicht um irgendwelche Methode, wie man
einen HAL aufsetzt.
Jörg W. schrieb:> Wobei, mit ein bisschen Mimik kann man sicher aus B6 ein "B6" machen,
Das müsste gehen, Stichwort "Stringification".
> und das wieder kann man mit "B6"[0] und "B6"[1] zugreifen. ;-)
Ok, dann hast Du die Character 'B' und '6'. Bei der '6' könnte man noch
'0' abziehen, um auf das Bit zu schließen. Aber wie man PORT und 'B'
konkatenieren soll, ist mir schleierhaft.
Frank M. schrieb:> Aber wie man PORT und 'B' konkatenieren soll, ist mir schleierhaft.
Geht deshalb nicht, weil es zwar eine "Stringification" im Preprocessor
gibt, aber keine "Destringification".
Aber hier der ultimative, absolut dreckige Trick, wie es doch geht:
Frank M. schrieb:> Aber wie gesagt: Schmutziger gehts nicht :-)
Yep, an sowas in der Art dachte ich dabei, war nur zu faul, es erst
noch auszuprobieren. Normalerweise würde ich's auch nicht so machen,
und bezüglich des eigentlichen Thread-Themas (geschachtelte Expansion
von Makros) hilft es auch nicht viel weiter.
Frank M. schrieb:> Aber wie gesagt: Schmutziger gehts nicht :-)
Ja, nun, was soll's, nur weil sich der Herr MISRA im Grabe umdreht? ;-)
Du hast immerhin eine sehr interessante Lösungsmöglichkeit aufgezeigt.
Konrad S. schrieb:> Ja, nun, was soll's, nur weil sich der Herr MISRA im Grabe umdreht? ;-)
Mit dem Herrn MISRA hat es weniger zu tun. Das schmutzige ist das
Ausnutzen der Tatsache, dass die Register PORTx, DDRx, PINx in einem AVR
jeweils mit Abstand 3 aufsteigend angeordnet sind.
Ich hätte selber eine Lösung bevorzugt, die aus C6 die Konstanten PORTC,
DDRC, PINC erzeugt hätte. Dann wäre das sauber. Das geht aber nicht mit
dem Standard-C-Preprocessor wegen fehlender "De-Stringification".
Frank M. schrieb:> Das schmutzige ist das> Ausnutzen der Tatsache, dass die Register PORTx, DDRx, PINx in einem AVR> jeweils mit Abstand 3 aufsteigend angeordnet sind.
Spezifische Eigenschaften eines bestimmten Prozessors bzw. einer
bestimmten Plattform auszunutzen um genau dafür die Hardwarezugriffe zu
schreiben finde ich nicht schmutzig. Es erfordert bezüglich Portabilität
natürlich schon erhebliche Aufmerksamkeit, selbst innerhalb der AVRs.
Das Speicher-Layout der SFRs ist schließlich keine zugesicherte
Eigenschaft der AVRs. Die ATxmegas unterscheiden sich ja auch schon
recht deutlich von den ATmegas.
Konrad S. schrieb:> Spezifische Eigenschaften eines bestimmten Prozessors bzw. einer> bestimmten Plattform auszunutzen um genau dafür die Hardwarezugriffe zu> schreiben finde ich nicht schmutzig.
Ich schon. Und zwar SEHR.
Und genau deshalb hab ich hier die ganze Zeit davon abgeraten. Es ist
in wirklich JEDEM Falle besser, schlichtweg die direkten
Hardwarezugriffe so zu schreiben, wie man es nach Lesen des RefManuals
tun würde - ohne Makros.
OK, manche Sachen müssen sein, wie z.B. sowas:
#define GPIOB_PSOR (*((volatile dword *) 0x400FF044))
aber darüber hinaus wird es nur fischig, schlecht lesbar und auch nicht
tatsächlich BESSER - und damit schlecht wartbar, bis hin zu sowas:
Karl H. schrieb:> Letzten Endes legt man sich beispielsweise so einen Satz Makros einmal> in einem Header File an, verbannt das auf ein Common Directory (auf dem> man alles mögliche wiederverwendbare Zeugs sammelt) und includiert von> dort ohne sich jemals wieder grossartig viele Gedanken über die dahinter> steckende Makro-Magie zu machen.
Allein bei dem Gedanken an sowas krieg ich nen Schreikrampf. Ja, es ist
wohl der Wunsch dahinter, ähem der Wunsch nach Ergebnis ohne sich
"grossartig viele Gedanken" machen zu müssen.
Aber wenn ich die letzten Beiträge von mec lese, merke ich, daß einige
hier rein garnix verstehen WOLLEN. Da ist wohl jeder Rat vergebens. Nee,
es ist nicht Herr oder Frau Misra, sondern einfach nur der Gedanke an
KISS. Keep it small and simple. Das ist mMn der einzige Grundsatz, der
auf lange Sicht wirklich Bestand hat.
le x. schrieb:> Das könnte dann z.B. so aussehen:> void Enable_Led(void)> {> i2c_write(LED_CHANNEL)> pin_set(LED_ENABLE_CHANNEL)> }
Besser:
1
voidDoMyAction(void)// war mal Enable_Led (void)
2
{boolr;
3
GPIOB_PSOR=1<<7;// aus
4
r=i2c_write(LED_ADDR,ActionCode);// mach mir mein ding..
5
if(r)GPIOB_PCOR=1<<7;// ein wenn ACK
6
}
Und warum so und nicht wie oben? Weil pin_set(LED_ENABLE_CHANNEL) als
Funktion mit konstantem Argument auftritt und an dieser Stelle eine
völlig unnütze Komplizierung darstellt - insbesondere wenn man es wie
Karlheinz macht. Dann wird sogar ne Bugschleuder draus.
Leute, denkt lieber daran, was ihr eurer Liebsten zu Weihnachten
schenken wollt anstatt hier der Makro-Onanie das Wort zu reden...
W.S.
W.S. schrieb:> Da ist wohl jeder Rat vergebens.
Nochmal, der Thread ging um Makros und deren geschachtelte Ersetzungen.
Wenn du dafür ein Beispiel jenseits der genannten hast, dann bring' es,
ansonsten mach' bitte deinen eigenen Thread auf, um über
Hardwareabstraktion zu philosophieren.
Ich bin mittlerweile dazu übergegangen das wie folgt zu lösen:
In der aller untersten Ebene meiner Abstraktion habe ich für jeden
CPU-Typ eine Definition eines struct das einen Pin auf der jeweiligen
Hardware vollständig beschreibt und einen Satz von inline-Funktionen die
darauf operieren, z.B.:
1
structpin{
2
// hier alle notwendigen Felder
3
// die einen Pin identifizieren,
4
// also Pointer auf alle notwendigen
5
// Register, zum Beispiel bei einem
6
// Kinetis sieht das so aus:
7
FGPIO_Type*constFPTx;
8
PORT_Type*constPORTx;
9
constu8number;
10
}
11
12
staticinlinevoidGPIO_set_high(conststructpinpin){
13
// hier ist der Code implementiert
14
// der auf dieser Hardware den Pin auf
15
// high setzt, zum Beispiel bei Kinetis:
16
pin.FPTx->PSOR=(1<<pin.number);
17
}
18
19
staticinlinevoidGPIO_set_low(conststructpinpin){
20
// und so weiter...
21
[...]
Der Vorteil ist nun ich kann die Pins auch innerhalb meiner
Treiberschichten ganz transparent und typsicher als Variablen
herumreichen, zum Beispiel kann ich meinem SPI-Teiber einfach beim
initialisieren den CS-Pin als Variable übergeben. Dieser dann wiederum
bedient sich der obigen Funktionen wenn er am Chip-Select wackeln will.
Dabei ist es ihm völlig egal wie das struct aussieht und was die
GPIO-Funktion eigentlich genau macht.
Darüber lege ich dann eine weitere dünne Schicht die so Sachen hat wie
1
voidled_pwr_on(){
2
GPIO_set_low(LED_PWR);
3
}
4
5
voidled_pwr_off(){
6
GPIO_set_high(LED_PWR);
7
}
oder auch (vereinfachtes Beispiel)
1
staticstructpinchip_select;
2
3
voidspi_init(pincs,foobar,whatever){
4
chip_select=cs;
5
6
// und anderer code
7
}
8
9
voidspi_select(){
10
GPIO_set_low(chip_select);
11
}
12
13
voidspi_unselect(){
14
GPIO_set_high(chip_select);
15
}
Mit -Os und -flto und der Constant-Propagation wird das übrigens alles
ganz hervorragend geinlined und zusammengeschrumpft auf wenige inline
ASM-Befehle pro Funktinsaufruf.
Bis hierhin ist alles plain C.
Das einzige was nun verbleibt ist das Erstellen der pin-Konstanten.
1
staticconststructpinHMT_CS={...whatever..};
2
staticconststructpinHMT_SCK={...whatever..};
3
staticconststructpinHMT_MISO={...whatever..};
4
staticconststructpinHMT_MOSI={...whatever..};
5
staticconststructpinHMT_INT={...whatever..};
6
staticconststructpinLED_PWR={...whatever..};
7
// und so weiter
Und genau hier an dieser Stelle wo viel repetitive Schreibarbeit
angesagt ist bediene ich mich der uralten fast schon in Vergessenheit
geratenen Technik der X-Macros. Anstatt all die vielen const struct da
oben von Hand hinzuschreiben erzeuge ich diesen Code mit X-Macros aus
simplen kurzen knackigen Listen ohne viel redundantes Geschreibsel:
(in einer separaten config datei)
Und schon hab ich meine Konstanten da stehen, sauber als reinen C-Code.
Und weils so schön ist erzeuge ich auch gleich mein GPIO_init()
ebenfalls mit einer Handvoll X-Macros, das kann ich machen da ich in
weiser Voraussicht die pins bereits in Listen einsortiert habe die ich
dann jeweils geeignet initialisieren kann.
Den Code dazu schreibe ich jetzt nicht hin, das kann der geneigte Leser
sich anhand des Gesagten nun einfach selbst erarbeiten, das ist nur noch
Fleißarbeit.
Somit schlage ich nun also 2 Fliegen mit einer Klappe: Es ist einfach zu
warten (wenig Schreibarbeit), es ist extrem flexibel und kann auf
praktisch jede beliebige Hardware portiert werden und es wird im Code
kein einziges Makro aufgerufen, keine üblen Makro-Hacks, kein
Macro-Verschachtelungen mit Stringify oder Komma-Splitten oder anderen
Vergewaltigungen, das ganze API ist nach außen hin plain C, inclusive
der eigentlichen Pin-Definitionen die als echte structs sogar typsicher
in Funktionsargumenten verwendet und als Variablen gespeichert werden
können.
Bernd K. schrieb:> In der aller untersten Ebene meiner Abstraktion
Das ist nicht das Thema dieses Threads, Leute!
Schaut euch doch mal den Beitrag des TE an.
Jörg W. schrieb:> Das ist nicht das Thema dieses Threads, Leute!>> Schaut euch doch mal den Beitrag des TE an.
Sorry, hab Deinen Einwand übersehen, hat sich zeitlich überschnitten.
Aber bitte nicht löschen, es hat eine halbe Stunde gekostet das
auszuformulieren.
Deshalb hab ich auch den Betreff geändert um einen Seitenthread zu
erzeugen.
Zum Thema: Ich dachte das sei bereits gelöst nach den ersten paar
Antworten: Der Code des Threaderstellers funktioniert nachweislich genau
so wie er ihn gepostet hat und wie er es sich gewünscht hat, es ist
unklar wie er darauf kommt es läge ein Problem vor, daher war ich der
Meinung dieses Thema wäre bereits seit Wochen abgeschlossen und wir
könnten zum gemütlichen Teil übergehen.
Bernd K. schrieb:> Aber bitte nicht löschen, es hat eine halbe Stunde gekostet das> auszuformulieren.
Wie fändest du es: wenn ich ihn lösche, bekommst du deinen Beitrag
ja per Mail zurück, und könntest damit einen neuen Thread eröffnen.
Wär' das OK?
Man kommt sonst einfach vom Hundertsten ins Tausendste.
Jörg W. schrieb:> Wie fändest du es: wenn ich ihn lösche, bekommst du deinen Beitrag> ja per Mail zurück, und könntest damit einen neuen Thread eröffnen.> Wär' das OK?
Du könntest ihn auch stehen lassen und wir lassen Gras drüber wachsen,
immerhin berührt er einen Aspekt der Makros der bislang nicht genannt
wurde und komplementiert somit Deinen eigenen Beitrag den Du in
Beitrag "Re: Define Makros in C" geschrieben hast
und in dem Du praktisch das selbe Problem auf andere Weise löst? Was
unterscheidet Deinen Beitrag von meinem in dieser Hinsicht?
Bernd K. schrieb:> Was unterscheidet Deinen Beitrag von meinem in dieser Hinsicht?
Dass meiner ein Beispiel dafür war, wofür man die doppelte Ersetzung
der Makros mal benötigen könnte. Er war nicht dafür gedacht, nun
unbedingt als Empfehlung für eine bestimmte Art von Hardwareabstraktion
zu dienen. (Mein Fehler war es, die diesbezügliche Diskussion, die
W.S. dann losgetreten hat, nicht gleich mit Verweis auf das Thema des
Threads abzuwürgen.)
Wie gesagt, im Sinne dieses Threads wäre ich viel stärker daran
interessiert, weitere Beispiele für solche doppelten Makroersetzungen
gezeigt zu bekommen. Ja, das Problem des TE ist gelöst, aber wenn den
Thread mal jemand findet, kann er zumindest dafür dann Anregungen
entnehmen.
Jörg W. schrieb:> Wie gesagt, im Sinne dieses Threads wäre ich viel stärker daran> interessiert, weitere Beispiele für solche doppelten Makroersetzungen
Nun, im weitesten Sinne kann man mein gezeigtes X-Macro ebenfalls als
doppelte Ersetzung betrachten.
1
#define LISTE \
2
X(foo, 42) \
3
X(bar, 23)
4
5
#define X(a,b) \
6
const baz_t a = b;
7
8
LISTE
9
10
#undef X
Zuerst wird LISTE ersetzt durch X(foo, 42) X(bar, 23) und im nächsten
Schritt wird X(a,b) ersetzt für jedes Listenelement.
Und im weiteren Verlauf meines Postings (zugegeben mit viel Vorwort und
Nachwort und Umschweif, aber nur um den Abstraktionsfundamentalisten
gleich von vornherein den Wind aus den Segeln zu nehmen) hab ich halt
mal eine praktische Anwendung dafür aufgezeigt im selben Themenbereich
der schon angesprochen wurde, nämlich die Erzeugung von vielen const
struct für viele Pins mit der simplen Notation Buchstabe, Pin-Nummer.
Jörg W. schrieb:> Wie gesagt, im Sinne dieses Threads wäre ich viel stärker daran> interessiert, weitere Beispiele für solche doppelten Makroersetzungen> gezeigt zu bekommen.
Diese waren auch hier notwendig:
Frank M. schrieb:> #define STRINGIFY(s) TO_STRING(s)> #define TO_STRING(s) #s
Uff. Gerade noch die Kurve gekriegt :-)
Jörg W. schrieb:> Nochmal, der Thread ging um Makros und deren geschachtelte Ersetzungen.>> Wenn du dafür ein Beispiel jenseits der genannten hast, dann bring' es,
Oh ja, genau DAS habe ich bereits geschrieben, nämlich: LASST SOWAS
BESSER KOMPLETT BLEIBEN. Genau das - und das meine ich sehr sehr
ernst.
Du und viele andee hier haben die unselige Mentalität, einen beim
Beackern seines Feldes festgefahrenen TO in seiner zumeist falschen
Furche zu belassen und ihm allenfalls Ratschläge zu geben, wie er in
derselben Furche ein paar Schritte weiterkommt. Dabei übersiehst du mit
Regelmäßigkeit das Sinnvollste, nämlich so einem TO zu sagen "Laß es,
geh in eine andere Furche, dort geht's problemlos weiter bis zu deinem
eigentlichen Ziel. Wir hatten das neulich schon mal (mit Yalu). Also
ruf dir den Ansatz des TO nochmal ins Gedächtnis:
Pet schrieb:> habe folgendes Testmakro> #define A 1> #define Test(x) x>> Aufruf: Test(A)> Ergebnis : A> Ich hätte aber gerne das 1 rauskommt. Hat jemand eine Idee wie ich das> Makro änder muss, damit ich 1 erhalte?
Er hätte gern, daß 1 rauskommt. Ah ja. Warum will er mit dem Kopf durch
die Wand? Ist das etwa sinnvoll? Nein, natürlich nicht. Hätte er sich
nicht damit aufgehalten, sondern sich seinem eigentlichen Ziel
zugewandt, wäre er längst weiter.
Zum Schluß kommt Bernd mit seinem Beitrag, der ihn ne halbe Stunde zum
Formulieren gekostet hat - eine halbe Stunde!!, um zu erklären zu
versuchen, wie er sich sowas ausbaldowert hat. Hat er jemals daran
gedacht, wie lange jemand anderes (oder er selbst nach 2..3 Jahren) in
seine Quelle reingucken muß wie das berüchtigte Schwein ins Uhrwerk, bis
er kapiert hat, was und wie hier eigentlich abgehen soll? Ganz gewiß
nicht. Sowas ist Programmierer-Akrobatik oder Husarenstückchen, je
nachdem wie man's benennen will. Aber es mach über alles die ganze
Quelle nicht besser, sondern schlechter. Gründe gegen sowas sind genug
genannt, man braucht beim Programmieren keine kühnen Helden, sondern
solides biederes Handwerk.
W.S.
W.S. schrieb:> LASST SOWAS BESSER KOMPLETT BLEIBEN.
Warum brüllst du denn so herum?
Wenn du dieses Feature nicht brauchst, dann lass es einfach. Du
musst sie ja nicht nehmen, aber du musst hier nicht den Moby jetzt
spielen und dann auch noch jeden anderen davon überzeugen wollen,
dass das alles Mist wäre, nur weil es dir nicht in deinen Kram passt.
W.S. schrieb:> Zum Schluß kommt Bernd mit seinem Beitrag, der ihn ne halbe Stunde zum> Formulieren gekostet hat - eine halbe Stunde!!
28 Minuten davon entfielen auf ordentliche Strukturierung und
Formatierung, die Prüfung von Rechtschreibung und Grammatik und nicht
zuletzt den Einbau von prophylaktischen Abwehrklauseln die Leuten wie
Dir die Argumente entkräften sollten noch bevor sie sie überhaupt
vorgebracht haben.
Leider hast Du sie nicht gelesen und kritisierst blind. Das ist schade.
Die Kurzfassung ist übrigens in meiner Antwort auf Jörg, 2 Postings
weiter.
Das ist übrigens ein guter Eignungstest fürs Vorstellungsgespräch: Wenn
einer kommt der sagt er kann fließend C seit zig Jahren macht ihm keiner
was vor und dann mit nem X-Makro der simpelsten Sorte überfordert ist
(um eine Ecke denken, Scripte die C-Code generieren aus Datentabellen,
Teufelszeug!) dann kann man an der Stelle gleich zur Verabschiedung
übergehen, denn das würde nur in Tränen enden.
Jörg W. schrieb:> Warum brüllst du denn so herum?
Ganz einfach: damit es durch die mit Fleiß zugehaltenen Ohren dennoch
durchkommt. Ich hab's gefühlte 100x geschrieben und du hast dir ebenso
gefühlte 100x die Ohren zugehalten, um's nicht hören zu müssen. Also
lassen wir's bleiben - es sei denn, irgend jemand schreibt in diesen
Thread etwas derart abstruses, daß es geradegerückt werden muß.
W.S.
W.S. schrieb:> Ganz einfach: damit es durch die mit Fleiß zugehaltenen Ohren dennoch> durchkommt. Ich hab's gefühlte 100x geschrieben [...]
Na weil ihr aneinander vorbei redet bzw. schreibt.
W.S. schrieb:> Jörg W. schrieb:>> Warum brüllst du denn so herum?>> Ganz einfach: damit es durch die mit Fleiß zugehaltenen Ohren dennoch> durchkommt. Ich hab's gefühlte 100x geschrieben und du hast dir ebenso> gefühlte 100x die Ohren zugehalten, um's nicht hören zu müssen. Also> lassen wir's bleiben - es sei denn, irgend jemand schreibt in diesen> Thread etwas derart abstruses, daß es geradegerückt werden muß.>> W.S.
Du hälst dir die Ohren auch zu, außer dass man es so nicht machen soll,
kommt von dir nichts, und Fragen an dich wie du es besser machen willst
weichst du aus.
In welcher Branche arbeitest du?
Ich hab mir jetzt noch einigen profesionellen Code von einem MCU
Hersteller angesehen, und da wird alles mögliche gemacht, mit und ohne
Makros, wahrscheinlich einfach nach vorliebe des jeweiligen
Programmierers.
mec schrieb:> Du hälst dir die Ohren auch zu, außer man es so nicht machen soll,> kommt von dir nichts, und Fragen an dich wie du es besser machen willst> weichst du aus.
zum 101. mal: erstens weiche ich nicht aus (ich mach das fast NIE, auch
wenn ich dadurch gezwungen bin, andere abzuwatschen), zweitens sag ich
nochmal: Es besser zu machen bedeutet in den allermeisten Fällen es
bleibenzulassen, also etwa so wie du zitiert hast: "man es so nicht
machen soll". Reicht das denn noch immer nicht aus?
Guck mal in einen anderen grad hier aufgekommenen Thread:
"Beitrag "ChibiOS 3.0.4 STM32F4 Discovery Port";
Jaja, wieder mal. Es geht nicht, weil der TO wahrscheinlich in seiner
tollen IDE mal wieder was vergessen oder nicht verstanden hat und weil
in irgendwelchen ach so allgemein gehaltenen *.h von irgend einer
CMSIS-Geschmacksvariante der ganze Quatsch zwar deklariert ist, selbiges
jedoch mit gefühlten 100 verschiedenen #ifdef je nach konkreter Hardware
verklausuliert ist - letzteres deshalb, weil so ein bescheuerter Makro
eben doch nicht so allgemein gehalten sein kann.
Solche Beispiele findest du hier zuhauf. Ein ganz besonders übles hatte
ich vor einigen Jahren: den USB-Treiber von Nuvoton für deren NUC120.
Wenn du mal richtig abkotzen willst, dann guck dir den an. Ein wahres
Meisterwerk chinesischer Makro-Künstler. Es ist ein Sammelsurium von
geschachtelten Makros und void Pointern. So richtig Klasse, wenn man
nach irgendwelchen Bugs suchen muß!
W.S.
W.S. schrieb:> Es ist ein Sammelsurium von geschachtelten Makros und void Pointern.
Niemand, absolut niemand würde sowas hier das Wort reden. (Hab' ich
auch schon gesehen.)
Aber ein Beispiel für die miserable Anwendung eines Werkzeugs
rechtfertigt es nicht, das Werkzeug selbst zu verdammen.