ein Bit aus DWORD bzw long int auslensen
-
akari schrieb:
@ten : Warum defines und printf benutzen, wenn es bessere Mittel wie Funktionen
das 'printf' ist nur zu demozwecken. man braucht es nicht, um bits zu extrahieren.
und warum sollte man für so'nen minicode eine funktion anlegen?
-
Hallo
nicht für die Ausgabe, sondern für das Auswerten
bool GetBit(int value, int bit) { return value & (1<<bit); }
Das ist übersichtlicher und typsicher. Im gegensatz zu deinem define. Und wer unbedingt drauf besteht kann die Funktion ja so anlegen, das der Compiler sie gut inlinen kann.
bis bald
akari
-
akari schrieb:
Das ist übersichtlicher und typsicher. Im gegensatz zu deinem define.
kann ich nicht nachvollziehen. so einen kleinen pippicode kann man nicht übersichtlicher machen, wenn man das #define mit falschen typen versorgt bekommt man mecker vom compiler und ob hinten 'bool' oder nicht steht ist auch wumpe. das #define erzeugt sowieso nur 0 oder 1
:xmas2:
-
Hallo
du gehst immer von dir und deinem kleinen, übersichtlichen Programm aus.
In einem großen Projekt, wo mehrere Leute dran arbeiten, sind Makros schlecht.
Zum einem kommt zwar ein Kompilerfehler, aber der ist wesentlich weniger aussagerkräftige als wenn ein falscher Typ an eine Funktion übergeben wird.Desweiteren sind Makros weder in Klassen noch in Namespaces gruppierbar, und damit kannst du schön böse Linkerfehler bekommen, wenn in verschiedenen Bibliotheken dasselbe Makro mit demselbem Namen vorhanden ist. Bei Funktionen kannst du diese mit Namespaces trennen, bei Makros eben nicht.
Deshalb gar nicht erst Makros als Funktionsersatz angewöhnen.
bis bald
akari
-
akari schrieb:
du gehst immer von dir und deinem kleinen, übersichtlichen Programm aus.
In einem großen Projekt, wo mehrere Leute dran arbeiten, sind Makros schlecht.
Zum einem kommt zwar ein Kompilerfehler, aber der ist wesentlich weniger aussagerkräftige als wenn ein falscher Typ an eine Funktion übergeben wird.Desweiteren sind Makros weder in Klassen noch in Namespaces gruppierbar, und damit kannst du schön böse Linkerfehler bekommen, wenn in verschiedenen Bibliotheken dasselbe Makro mit demselbem Namen vorhanden ist. Bei Funktionen kannst du diese mit Namespaces trennen, bei Makros eben nicht.
... alles richtig was du da schreibst, aber -
akari schrieb:
Deshalb gar nicht erst Makros als Funktionsersatz angewöhnen.
das habe ich auch nicht versucht. was isch da geposted habe *ist* ein makro, kein ersatz für eine funktion...
:xmas2:
-
Hallo
Das Sprachmittel in C/C++ zum Modularisieren von Implementationen sind nunmal Funktionen, nicht Makros. Benutzt du ein Makro, um den Effekt einer Funktion zu haben ohne Zusatznutzen durch die Makroeigenschaft zu bekommen, ist das Makro ein ungünstiger Funktionsersatz.
bis bald
akari
-
akari schrieb:
Benutzt du ein Makro, um den Effekt einer Funktion zu haben ohne Zusatznutzen durch die Makroeigenschaft zu bekommen, ist das Makro ein ungünstiger Funktionsersatz.
aber das mache ich nicht. natürlich kann man dieses makro auch als funktion schreiben aber das ist genau so doof wie aus einer funktion mit aller gewalt ein makro machen zu wollen (ausnahmen bestätigen auch hier die regel).
vorteile dieses makros:
es ist klein, es ist leicht verständlich.
99.999% der c und c++ compiler dieser welt werden genau das tun was man will, einige wenige instructions in den code an die stelle einfügen, an die man's haben will.wie du schon richtig schreibst: makros sind kein ersatz für funktionen - aber umgekehrt ist es eben auch so...
:xmas2:
-
ten schrieb:
vorteile dieses makros:
es ist klein, es ist leicht verständlich.In der Verständlichkeit tun sich Makro und Inline-Funktion IMHO nichts, die Funktion mag manchem C-Unerfahrenen gar übersichtlicher erscheinen (erst bem allgemeineren Funktionstemplate wirds komisch :)):
#define GET_BIT(value,bit) !!((value)&(1<<(bit))) inline bool GET_BIT (int value, int bit) { return value & (1 << bit); } template <typename T> inline bool GET_BIT (T value, T bit) { return value & (1 << bit); }
ten schrieb:
99.999% der c und c++ compiler dieser welt werden genau das tun was man will, einige wenige instructions in den code an die stelle einfügen, an die man's haben will.
Klar, wenn das Ding auch in C Verwendung finden soll, ist das Makro besser, da es auf jeden Fall inline expandiert wird. Aber ich wüßte keinen C++-Compiler, der eine solch kleine Funktion nicht inlinen könnte. Und daß die Funktionsvariante in C++ allgemein und, soweit sie einen vollwertigen Ersatz darstellt, allgemein aus bekannten Gründen (Typsicherheit, Namespace-Kompatibilität, Vermeidung von Fehlern mit bei der Übergabe modifizierten Parametern etc.) vorzuziehen ist, sollte hoffentlich klar sein, nicht zuletzt wegen der Gefahr, daß Neulinge sich solches allgemein angewöhnen.
Als dummes, aber leider allzu realistisches Beispiel nimm einen kleinen Tippfehler in der Makro- bzw. Funktionsdefinition. Ist es eine Funktion, so sagt mir der Compiler ihren Namen und die korrekte Fehlerstelle. Ist es aber ein Makro, so tritt der Fehler (in verzwickteren Fällen sogar nur manchmal) bei der Verwendung des Makros auf, ohne daß eine Meldung anzeigen würde, daß der Fehler im Makro liegt. Warum also Makros verwenden, wenn Funktionen das Gleiche können und in allen Aspekten mehr Sicherheit bieten?ten schrieb:
wie du schon richtig schreibst: makros sind kein ersatz für funktionen - aber umgekehrt ist es eben auch so...
Zweifellos. Die eigentlichen Zwecke der Makros in C++ - Include-Guarding und plattform- und compilerspezifische Unterscheidungen - will ja auch niemand durch Funktionen ersetzen.
-
Folgendes zum Thema Makro:
Grundsätzlich hat Akari recht. Makros sind nicht zwingend lesbarer und machen oft auch das Debuggen nicht leichter. Allerdings ist es - je nach Anwendung und richtig angelegt - extrem nützlich. Grade im Embedded-Bereich benutzt man oftmals Makros für einfache Befehle wie zum Beispiel Bits setzen, prüfen oder löschen. Hintergrund ist der, dass diese Operationen auf keinen Fall ein Funktionsaufruf dulden. Grad im Embeddedbereich werden gewisse Sequenzen vom Compiler erkannt und in dedizierte BitSet und BitClear-Befehle umgemünzt. Solch ein Makro hilft dabei, immer die entsprechenden Compileroptimierungen zu benutzen ohne sich darauf verlassen zu müssen, dass der Compiler intelligent genug ist, es selber zu merken. Ausserdem - wenn der Compiler die Funktion nicht inlined, hast du das Probelm, dass bei jedem Mal Bitabfragen der Stack gesichert wird und ein Funktionsaufruf gemacht wird. Das heisst frappante Performance-Einbussen.Man darf also Makros nicht generell verteufeln. Es ist wie mit jeder Regel: Der Verstand definiert die Ausnahmen. Genau so wie der Verstand durchaus auch ein goto erlauben kann. Der Grund wieso das Anfängern immer verboten wird ist a) weil mans in 99.99% der Fälle eh anders lösen kann und b) weil der Anfänger schlicht die Folgen nicht abschätzen vermag und man ihn deshlab vor ungeschickten Fallstricken und Nebeneffekten schützenwill.
Gegen oben angeführtes Makro ist eigentlich auch ncihts einzuwenden, zumals nach allen Regeln der Makrokunst aufgebaut ist:
- Eindeutig als Makro erkennbar gemacht (Grossschreibung)
- Jedes Argument ist für sich immer umklammert um Nebeneffekte zu vermeiden
- Der Makroausdruck ist nochmals geklammert um Nebeneffekte zu vermeiden
Einzig die beiden Negierungen erschliessen sich mir noch nicht ganz.
#define GET_BIT(value,bit) ((value)&(1<<(bit)))
Müsste das nicht ausreichen?
-
junix schrieb:
Einzig die beiden Negierungen erschliessen sich mir noch nicht ganz.
#define GET_BIT(value,bit) ((value)&(1<<(bit)))
Müsste das nicht ausreichen?
nee, die erste negierung macht einen bool'schen wert daraus (0 oder 1) aber leider eben negiert, deshalb noch ein zweites 'nicht', damit's wieder richtig rum ist.
...und danke für die guten argumente für makros
ich hätt's nicht so treffend schreiben können.btw: weil wir hier im c++ forum sind - als template funktion kann man's auch gelten lassen. ein template vereint ja den vorteil von makros und typsicherheit etc.
-
junix schrieb:
Grade im Embedded-Bereich benutzt man oftmals Makros für einfache Befehle wie zum Beispiel Bits setzen, prüfen oder löschen. Hintergrund ist der, dass diese Operationen auf keinen Fall ein Funktionsaufruf dulden. Grad im Embeddedbereich werden gewisse Sequenzen vom Compiler erkannt und in dedizierte BitSet und BitClear-Befehle umgemünzt. Solch ein Makro hilft dabei, immer die entsprechenden Compileroptimierungen zu benutzen ohne sich darauf verlassen zu müssen, dass der Compiler intelligent genug ist, es selber zu merken. Ausserdem - wenn der Compiler die Funktion nicht inlined, hast du das Probelm, dass bei jedem Mal Bitabfragen der Stack gesichert wird und ein Funktionsaufruf gemacht wird. Das heisst frappante Performance-Einbussen.
Mag sein, aber gerade im Embedded-Bereich benutzt man zumeist C- oder beschnittene C++-Compiler, wo Makros sicher ihre Berechtigung haben. Das Hauptanwendungsgebiet von C++ wird aber doch von besseren Compilern abgedeckt, die mindestens solche Funktionen inlinen können dürften. Aber egal
Und was die doppelte Negierung angeht: entspricht nicht jeder Wert != 0 dem boolschen true? Irgendwie kann ich mir gerade keinen Fall vorstellen, wo die doppelte Negierung Vorteile brächte...
Abschließend kann ich nur das hier unterschreiben:
ten schrieb:
btw: weil wir hier im c++ forum sind - als template funktion kann man's auch gelten lassen. ein template vereint ja den vorteil von makros und typsicherheit etc.
-
junix schrieb:
Makros ... machen oft auch das Debuggen nicht leichter.
inline funktionen ebenso wenig. ich mache öfters mal mit codewarrior-compilern rum. die erzeugen an sich schon ziemlich aggressiv optimierten code, wenn man die dann noch zwingt eine funktion zu inlinen, dann gibt's nicht selten meldungen wie:
"removed dead code" und "debug info incorrect because of inline expansion"
:xmas2:
-
ten schrieb:
nee, die erste negierung macht einen bool'schen wert daraus (0 oder 1) aber leider eben negiert, deshalb noch ein zweites 'nicht', damit's wieder richtig rum ist.
Dann wäre das entweder noch in Klammern zu packen oder gleich auszulassen. Denn tatsächlich liegt audacia da richtig: Das geht auch ohne Negierung.
audacia schrieb:
Mag sein, aber gerade im Embedded-Bereich benutzt man zumeist C- oder beschnittene C++-Compiler, wo Makros sicher ihre Berechtigung haben. Das Hauptanwendungsgebiet von C++ wird aber doch von besseren Compilern abgedeckt, die mindestens solche Funktionen inlinen können dürften. Aber egal
Oh, Embeddedcompiler sind vermutlich viel stärker optimiert als normale Compiler. Ich wäre hier also mit wertenden Aussagen wie "besser" vorsichtig. (o: Grundsätzlich war der Verweis auf die Embeddedszene auch eher als Ursachenforschung zu sehen. Und wie gesagt: Grundsätzlich ist gegen Makros nichts einzuwenden, wenn man weiss was man tut.
audacia schrieb:
Und was die doppelte Negierung angeht: entspricht nicht jeder Wert != 0 dem boolschen true? Irgendwie kann ich mir gerade keinen Fall vorstellen, wo die doppelte Negierung Vorteile brächte...
richtig.
ten schrieb:
inline funktionen ebenso wenig. ich mache öfters mal mit codewarrior-compilern rum.
Naja, die sind auch etwas beschränkt (o;
-
junix schrieb:
Denn tatsächlich liegt audacia da richtig: Das geht auch ohne Negierung.
nicht wirklich...
if (1 == GET_BIT(0xffff, 15)) { .... }
würde ohne die negierungen nicht klappen.
die negierungen sind gewissermassen wie ein type-cast nach 'bool'junix schrieb:
ten schrieb:
inline funktionen ebenso wenig. ich mache öfters mal mit codewarrior-compilern rum.
Naja, die sind auch etwas beschränkt (o;
naja, von der bedienung ist's vielleicht etwas hakeleig aber der erzeugte code lässt alle gcc-derivate alt aussehen. was 'n codewarrior ausspuckt ist um den faktor 2..3 mal schneller und kompakter...
:xmas2:
-
ten schrieb:
junix schrieb:
Denn tatsächlich liegt audacia da richtig: Das geht auch ohne Negierung.
nicht wirklich...
if (1 == GET_BIT(0xffff, 15)) { .... }
würde ohne die negierungen nicht klappen.
die negierungen sind gewissermassen wie ein type-cast nach 'bool'Weshalb man ja auch einfach
if (GET_BIT(0xffff, 15))
schreibt (o;
-
junix schrieb:
Weshalb man ja auch einfach
if (GET_BIT(0xffff, 15))
schreibt (o;
kann man machen, aber dann würde ich das makro besser 'BIT_TEST' taufen
ohne die negierungen ist es einfach anfälliger für fehler. ein GET_BIT makro sollte doch besser zwei genau definierte zustände ausspucken und nicht '0 oder irgendeine beliebige zweierpotenz'...
:xmas2:
-
Stimmt, ich taufe meine Markos auch immer "mIsBitSet", "mSetBit" und "mClrBit" und nicht GET_BIT (o;
ten schrieb:
ein GET_BIT makro sollte doch besser zwei genau definierte zustände ausspucken und nicht '0 oder irgendeine beliebige zweierpotenz'...
Naja, standardmässig definiert ist eigentlich nur TRUE ist alles ausser false, und FALSE ist 0. Von daher liefert das Makro ohne Invertierung brav ein boolsches Ergebnis. Denn wenn du schon so päpstlich sein willst, dann ist 1 nicht boolsch sondern integer und das Selbe gilt für 0. In C wiederum gibts nämlich auch keine definitionen dafür.
Aber wenn dus ganz päpstlich willst, dann wäre ein solches Makro Konsequent:
#define GET_BIT(value,bit) (((value)&(1<<(bit))) > 0 ? TRUE : FALSE)
Ob das allerdings dem Compiler bei der optimierung hilft...? Der Trick am boolschen ist ja eigentlich der das im Assembler auf "not null" oder eben "null" geprüft wird. (zumindest gilt das für meine IAR Compiler)
-
junix schrieb:
Stimmt, ich taufe meine Markos auch immer "mIsBitSet", "mSetBit" und "mClrBit" und nicht GET_BIT (o;
au weia, gross/klein gemischt. das sieht ja dann wie 'ne funktion aus
junix schrieb:
Denn wenn du schon so päpstlich sein willst, dann ist 1 nicht boolsch sondern integer und das Selbe gilt für 0. In C wiederum gibts nämlich auch keine definitionen dafür.
1 und 0 ist beides, bool wie auch int
...und C99 kennt ja auch schon 'bool'junix schrieb:
Aber wenn dus ganz päpstlich willst, dann wäre ein solches Makro Konsequent:
#define GET_BIT(value,bit) (((value)&(1<<(bit))) > 0 ? TRUE : FALSE)
Ob das allerdings dem Compiler bei der optimierung hilft...? Der Trick am boolschen ist ja eigentlich der das im Assembler auf "not null" oder eben "null" geprüft wird. (zumindest gilt das für meine IAR Compiler)
ach nee, besser nicht. dann machste dich davon abhängig wie TRUE und FALSE definiert sind und wenn man pech hat, muss der compiler einen branch mit einbauen. nicht ganz unwichtig an so'nem makro ist ja auch, dass es mit wenigen instructions zeitlich linear abgefrühstückt wird. ein sprung da drin könnte in zeitkritischen codes (wenn's etwa um bruchteile von mikrosekunden geht) schon übel sein, von funktionsaufrufen ganz zu schweigen (aber das haben wir ja schon hinter uns gelassen)
:xmas2:
-
ten schrieb:
junix schrieb:
Stimmt, ich taufe meine Markos auch immer "mIsBitSet", "mSetBit" und "mClrBit" und nicht GET_BIT (o;
au weia, gross/klein gemischt. das sieht ja dann wie 'ne funktion aus
deshalb die Markierung mit "m". Eine Funktion würde "IsBitSet" heissen.
junix schrieb:
Aber wenn dus ganz päpstlich willst, dann wäre ein solches Makro Konsequent:
#define GET_BIT(value,bit) (((value)&(1<<(bit))) > 0 ? TRUE : FALSE)
Ob das allerdings dem Compiler bei der optimierung hilft...? Der Trick am boolschen ist ja eigentlich der das im Assembler auf "not null" oder eben "null" geprüft wird. (zumindest gilt das für meine IAR Compiler)
ach nee, besser nicht. dann machste dich davon abhängig wie TRUE und FALSE definiert sind und wenn man pech hat, muss der compiler einen branch mit einbauen.
ten schrieb:
nicht ganz unwichtig an so'nem makro ist ja auch, dass es mit wenigen instructions zeitlich linear abgefrühstückt wird. ein sprung da drin könnte in zeitkritischen codes (wenn's etwa um bruchteile von mikrosekunden geht) schon übel sein,
Stimmt... Aber widerspricht das nicht etwas deiner Haltung lieber noch zwei Negierungen einzufügen? Ich würde mal behaupten der Compiler schmeisst die sowieso schon raus. Habs mal mit nem Compiler (mangels was anderem) für Atmel AVR getestet. Keine Optimierungen eingeschaltet. Hab den C-Code mal als Kommentar in die Ausgabe einfügen lassen. Hier mein Vorschlag (hald ohne Makro, macht ja aber keinen Unterschied)
- SBRC ist Skip if Register Bit is Zero ( Direkter Bitbefehl )
// 5 unsigned volatile char test=0x80; LDI R16, 128 ST Y, R16 // 6 // 7 if (test & (1 << 4)) LD R16, Y SBRC R16, 4 // 8 { // 9 __no_operation(); NOP // 10 }
Deine Variante:
// 5 unsigned volatile char test=0x80; LDI R16, 128 ST Y, R16 // 6 // 7 if (!!(test & (1 << 4))) LD R16, Y SBRC R16, 4 // 8 { // 9 __no_operation(); NOP // 10 }
Wie man sieht wird deine doppelte Negierung direkt übersprungen.
Der Trick soll ja grade sein, dass keine unnötigen Rechenoperationen stattfinden und man deshlab direkt auf die BIts testet.
Leider krieg ich mein dev-cpp nicht dazu Assembler auszuspucken...
-
ten schrieb:
junix schrieb:
Makros ... machen oft auch das Debuggen nicht leichter.
inline funktionen ebenso wenig. ich mache öfters mal mit codewarrior-compilern rum. die erzeugen an sich schon ziemlich aggressiv optimierten code
Beim Debuggen schaltet man Optimierungen i.d.R. aus