ein Bit aus DWORD bzw long int auslensen



  • 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 😉



  • junix schrieb:

    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.

    ja, die zwei negierungen sorgen dafür, dass es 'logisch richtig' ist, fliegen aber im erzeugten maschinencode komplett raus, wenn man den output von GET_BIT auf wahr oder falsch überprüft (if, while, ect.). wenn man GET_BIT() einer variablen zuweist, z.b. "int x = GET_BIT(...);", dann sorgt der compiler dafür, dass nix anderes als 0 oder 1 in dieser variablen landet. natürlich erkennt er auch hierbei die beiden negierungen direkt hintereinander und kann das sehr gut optimieren...


Anmelden zum Antworten