switch-case mit 4-Byte-Strings

OP #2150203
Lesenswert?

Hallo,

folgendes Problem:

uint32_t cmd;  //4 Bytes

cmd = LeseCmd(); //4 bytes

switch (cmd)
{
   case "ABCD"   //4 bytes, (ohne \0)
      ....;
}



Ich hätte hier gerne, dass beim kompilieren als Case-Vergleichswert der 
entsprechende ASCII-Code 12345678 benutzt wird, ohne dass ich diesen per 
Hand "ausrechnen" muss.

Für jeden dezenten Denkanstoß bin ich überaus dankbar.

PS:
'A' + ('B'<<8)+ ('C'<<16)+ ('D'<<24) geht, gefällt mir aber nicht.
Gast #2150298
Lesenswert?

Nein, jetz mal im Ernst:

Mir ist keine Präprozessor Anweisung bekannt die sowas leistet
und im case-Statement muss halt mal eine Konstante stehen

Aber machs halt so:

#define _ABCD    ('A' + ('B'<<8)+ ('C'<<16)+ ('D'<<24))
#define _ABC1    ('A' + ('B'<<8)+ ('C'<<16)+ ('1'<<24))
#define _ABC2    ('A' + ('B'<<8)+ ('C'<<16)+ ('2'<<24))
#define _ABC3    ('A' + ('B'<<8)+ ('C'<<16)+ ('3'<<24))
.
.
.


uint32_t cmd;  //4 Bytes

cmd = LeseCmd(); //4 bytes

switch (cmd)
{
   case _ABCD:        ....;
   case _ABC1:        ....;
   case _ABC2:        ....;
   case _ABC3:        ....;
      ....;
}

dann sieht dein Programm wenigstens schön aus


Viel Spass noch !!!
Gast #2150356
Lesenswert?

Rolle schrieb:
> Peter Dannegger schrieb:
>> #define MK32(a,b,c,d) (((uint32_t)'a')<<24 | ((uint32_t)'b')<<16 | 'c'<<8 | 
'd')
>> ...
>> case MK32(A,B,C,D):
>
>
> Das ist mal schnell schön hingeschrieben, geht aber nicht !!!
>
>
>
>     Beitrag melden | Bearbeiten | Löschen |

geht bei gcc, OK, aber nicht allen Compilern
#2150361
Lesenswert?

@  Rolle (Gast)

>Das ist mal schnell schön hingeschrieben, geht aber nicht !!!

Weil der Peter ein paar Striche vergessen hat. So sollte es laufen.
1
#define MK32(a,b,c,d) (((uint32_t)'a')<<24 || ((uint32_t)'b')<<16 || 'c'<<8 || 'd')
2
...
3
case MK32(A,B,C,D):

MFG
Falk
#2150415
Lesenswert?

1
#define MAKEK32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned int)'b')<<16 || 'c'<<8 || 'd')
2

3
int main(void) {
4

5
  unsigned int  x;
6
  x = (((unsigned int)'X')<<24 || ((unsigned int)'Y')<<16 || 'Z'<<8 || 'A'); //Test
7
  switch (x) {
8

9
    case MAKE32(A,B,C,D):  printf("Falscher case");
10
                break;
11
    case MAKE32(X,Y,Z,A):  printf("Funktioniert");
12
                break;
13
    default:        break;
14
  }
15

16
  return EXIT_SUCCESS;
17
}

Funktioniert bei mir auch mit GCC nicht. Also ich glaube auch dass die 
Textersetzung mittels Makro nicht erlaubt ist und bei der case anweisung 
nur ganzzahlige Konstanten stehen dürfen.

-> Was spricht gegen einen if-Rechen


Mfg Michi
Gast #2150421
Lesenswert?

Michael Rathmair schrieb:
> Funktioniert bei mir auch mit GCC nicht.
geht nocht oder compiliert nicht? Wenn ja welche Fehlermeldung

> Also ich glaube auch dass die
> Textersetzung mittels Makro nicht erlaubt ist und bei der case anweisung
> nur ganzzahlige Konstanten stehen dürfen.
die Preprozessor ist es egal wo er etwas ersetzt, er kennt kein switch.
nach dem Preprozessor steht ja dort eine Konstante.
#2150440
Lesenswert?

Oh sorry, sorry sorry für die Übereile.
Man sollte mal richtig tippen können.

Michael Rathmair schrieb:
> #define MAKEK32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned int)'b')<<16 || 
'c'<<8 || 'd')

muss natürlich
#define MAKE32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned 
int)'b')<<16 || 'c'<<8 || 'd')

sein.
1
#define MK32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned int)'b')<<16 || 'c'<<8 || 'd')
2
...
3
unsigned int  x;
4
  x = (((unsigned int)'X')<<24 || ((unsigned int)'Y')<<16 || 'Z'<<8 || 'A'); //Test
5

6
  switch (x) {
7

8
    case MK32(A,B,C,D):  printf("Falscher case");
9
                break;
10
    case MK32(X,Y,Z,A):  printf("Funftioniert");
11
              break;
12
    default:        break;
13
  }
bringt trotzdem duplicate case value

Mfg
Gast #2150457
Lesenswert?

Michael Rathmair schrieb:
> -> Was spricht gegen einen if-Rechen
Beim einen werden Zahlen verarbeitet, beim anderen knechtet er mit 
Strings. Das ist beim PC relativ egal, ein posierlicher µC hat daran 
verdammt viel zu tun.
Egal ob PC oder nicht, solange es ohne String-geklöppel geht, würde ich 
es ohne machen.

Im übrigen sollte die Textersetzung der GCC gar nicht mitbekommen? Der 
Präprozessor kommt doch vor dem Compiler.

Michael Rathmair schrieb:
> A. K. schrieb:
>> Klar doch. (x || 'A') ist immer 1.
>
> So gesehen schon dann funktioniert die Lösung aber doch nicht richtig
> ?????
Wo ist denn der Unterschied zwischen '|' und '||'?
oder
Wo ist denn der Unterschied zwischen '&' und '&&'?
#2150472
Lesenswert?

Peter Dannegger schrieb:
> Stimmt, die ' sind falsch, es geht nur so:#define MK32(a,b,c,d) 
(((uint32_t)a)<<24 | ((uint32_t)b)<<16 | c<<8 | d)
> ...
> case MK32('A','B','C','D'):

Das sieht gut aus !

ole schrieb:
> Wo ist denn der Unterschied zwischen '|' und '||'?
> oder
> Wo ist denn der Unterschied zwischen '&' und '&&'?

Oh je nochmal sorry. Klar weiß ich dass das '||' eine ein logische und 
das '|' eine bitweise Operation ist und in diesem Fall eine bitweise 
Operation richtig ist.
Gast #2150489
Lesenswert?

Michael Rathmair schrieb:
> Oh je nochmal sorry. Klar weiß ich dass das '||' eine ein logische und
> das '|' eine bitweise Operation ist
Wobei du nicht der erste warst dem der Fehler passiert ist, das war wohl 
Falk, aber dem müssen wir nicht den Unterschied erklären :-)
Macht halt jeder mal einen Fehler, vor allem wenn man wie bei so Fragen 
mal schnell 'trocken' codiert, also ohne das dann zu compilieren 
und/oder auszuprobieren.
#2150702
Lesenswert?

@  U.R. Schmitt (Gast)

>> Oh je nochmal sorry. Klar weiß ich dass das '||' eine ein logische und
>> das '|' eine bitweise Operation ist
>Wobei du nicht der erste warst dem der Fehler passiert ist, das war wohl
>Falk, aber dem müssen wir nicht den Unterschied erklären :-)

Ohhhh vor den kopfklatsch.

Da hatte ich wohl einen Dreher im Hirn. Und ich hab mich gestern ne 
halbe Stunde gefragt, warum bei meiner Simulation Unsinn rauskommt. 
Naja.

MFG
Falk
OP #2151145
Lesenswert?

Peter Dannegger schrieb:
1
 #define MK32(a,b,c,d) (((uint32_t)'a')<<24 | ((uint32_t)'b')<<16 |
2
 'c'<<8 | 'd')
3
 ...
4
 case MK32(A,B,C,D):

Das gefällt mir so ganz gut (natürlich inkl. Anführungszeichen an der 
richtigen Stelle ;-), aber mir ist noch etwas eingefallen:

Das ganze soll im Sinne von Modem-AT-Befehlen arbeiten. Wenn ich nun 
aber ca. 100 Befehle bearbeite, wird immer die komplette 4-Byte Variable 
verglichen. Ich weiß zwar nicht, was ein "if-Rechen" ist, aber ich 
glaube, dass damit ein Zweig im Sinne eines Stammbaumes gemeint ist. 
Jedenfalls führt letzteres zu einer enorm schnelleren Verarbeitung, da 
zB. viele Befehle mit 's' bzw. 'g' (set/get) beginnen:

1
switch (cmd>>24) {
2

3
    case 'A':  
4

5
        switch (cmd>>16) {
6

7
            case 'C':  printf("AC");
8
                        break;
9
            case 'D':  printf("AD");
10
                        break;
11
            default:   printf("Ax");
12
                        break;
13
          }
14

15
    case 'B':  
16

17
        switch (cmd>>16) {
18

19
            case 'C':  printf("BC");
20
                        break;
21
            case 'D':  printf("BD");
22
                        break;
23
            default:   printf("Bx");
24
                        break;
25
          }
26
    default:   printf("xx");
27
                break;
28
  }

Ich hatte gehofft, dass der super gcc auch dieses herausfindet und somit 
den ersten Buchstaben nur einmal vergleicht usw., tut er aber nicht. Es 
bleibt mir also wohl doch nur die Möglichkeit, die Befehle aufzudröseln.

Allen Beteiligten vielen Dank für die Anregungen.
#2151212
Lesenswert?

Johannes K. schrieb:
> den ersten Buchstaben nur einmal vergleicht usw., tut er aber nicht.

Mir ist zwar nicht klar, was genau du damit meinst, aber bei deinem 
Beispiel fehlen
1) die breaks hinter den "Sub-Switches"
2) das Ausmaskieren eines Zeichens im switch ((cmd>>16) ist nicht das 
zweite Zeichen, sondern die ersten beiden)

Zusatz zu 2): wenn du jeweils nach einzelnen Buchstaben vorgehst, wozu 
dann diese vorher überhaupt zu einem uin32_t kombinieren?
OP #2151275
Lesenswert?

Stefan Ernst schrieb:

> Mir ist zwar nicht klar, was genau du damit meinst, aber bei deinem
> Beispiel fehlen
> 1) die breaks hinter den "Sub-Switches"

Da hast du natürlich Recht.

> 2) das Ausmaskieren eines Zeichens im switch ((cmd>>16) ist nicht das
> zweite Zeichen, sondern die ersten beiden)

Ich meine, dass er immer nur 1 Byte benutzt, da ich ja mit nur 1 
vergleiche. Daher verwendet der Compiler dann doch das zweite.

>
> Zusatz zu 2): wenn du jeweils nach einzelnen Buchstaben vorgehst, wozu
> dann diese vorher überhaupt zu einem uin32_t kombinieren?

Mache ich dann natürlich nicht, war jetzt im Beispiel einfacher.
#2151290
Lesenswert?

Johannes K. schrieb:
> Ich meine, dass er immer nur 1 Byte benutzt, da ich ja mit nur 1
> vergleiche. Daher verwendet der Compiler dann doch das zweite.

Da meinst du falsch. Du vergleichst keine einzelnen Bytes, sondern 
komplette Zahlen.
1
uint16_t a = 0xabcd;
2
uint8_t b = 0xcd;
3

4
if (a==b) ...
Der Vergleich ist natürlich false, und nicht etwa (wie du meinst) true, 
nur weil der untere Teil von a mit b übereinstimmt.
OP #2151348
Lesenswert?

Stefan Ernst schrieb:
> Da meinst du falsch. Du vergleichst keine einzelnen Bytes, sondern
> komplette Zahlen.

Ok, hast natürlich wieder Recht. Ich würde also noch nach uint8_t casten 
müssen, wenn ich es so verwenden wollte. Richtig?

Meine Lösung sieht nun so aus (Auch, wenn mir Vergleiche mit "SE", "SD", 
usw. lieber gewesen wäre):

1
void ConfigCheckCommand()
2
{
3
    if (UART0_receive_line())
4
    {
5
        switch (uart0_rx[0])
6
        {
7
        case 'S':
8

9
            switch (uart0_rx[1])
10
            {
11
            case 'E':
12
                HMPowerOn();
13
                break;
14
            case 'D':
15
                HMPowerOff();
16
                break;
17
            case 'R':
18
                LampenEnable(&raumlicht, 1);
19
                break;
20
            case 'L':
21
                LampenEnable(&leselampe, 1);
22
                break;
23
            }
24

25
            break;
26

27
        case 'G':
28

29
            switch (uart0_rx[1])
30
            {
31
            case 'P':
32
                UART0TransmitByte(power_on);
33
                break;
34
            case 'R':
35
                UART0TransmitByte(LampenGet(&raumlicht));
36
                break;
37
            case 'L':
38
                UART0TransmitByte(LampenGet(&leselampe));
39
                break;
40
            }
41

42
            break;
43
        }
44
    }
45
}
#2151410
Lesenswert?

Johannes K. schrieb:

> Was willst du mir damit sagen?
> Dass der GCC es doch erkennt und alle 'Axxx' zusammenfast und alle
> 'Bxxx'?

Lex/Flex sind Codegeneratoren, denen man ein spezielle Quellfile 
vorsetzt und die daraus C Code produzieren. Das Ergebnis ist eine 
C-Funktion, die nicht mehr einzelne Zeichen zurück gibt, sondern fertige 
Tokens die für eine beliebige Anzahl Zeichen eines bestimmten Musters 
stehen. Und das recht zeiteffizient.

Aus der Lex-Beschreibung
1
"ABCD"  { return 1; }
2
"xyz"   { return 2; }
3
[0-9]+  { yylval = atoi(yytext); return 3; }
4
.       { crash("Trottel!"); }
wird Code erzeugt, der aus einem irgendwoher sequentiell gelesenen 
Zeichenstrom beim String "ABCD" den Wert 1 zurück gibt, bei "xyz" 2, bei 
jeder nicht negativen dezimalen Ganzzahl 3 (und deren Wert sich in der 
Variable yylval befindet). Die letzte Zeile ist sowas wie das default: 
vom switch.

Das sind gern benutze Tools, um Programmier- oder Kommandosprachen in 
ihre lexikalischen Einzelteile wie Schlüsselworte, Zahlen, Strings und 
Namen zu zerlegen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren