Wat is spaghetticode?

Kun je een kort voorbeeld posten van echte, overdreven spaghetti-code, misschien wat het doet? Kun je me de nachtmerrie van een kleine debugger laten zien?

Ik bedoel niet IOCCC-code, dat is sciencefiction. Ik bedoel voorbeelden uit het echte leven die je zijn overkomen…

Bijwerken

De focus is veranderd van “post wat spaghetti-code” naar “wat is preciesspaghetti-code?”. Vanuit historisch perspectief lijken de huidige keuzes te zijn:

  • oude Fortran-code die massaal berekende gotos gebruikt
  • oude Cobol-code met de instructie ALTER

Antwoord 1, autoriteit 100%

Voor mij is een meer modernvoorbeeld van spaghetticode wanneer je 20 dll’s hebt en elke DLL op de een of andere manier naar elkaar verwijst. Je afhankelijkheidsgrafiek ziet eruit als een enorme klodder en je code springt overal heen zonder echte volgorde. Alles is onderling afhankelijk.


Antwoord 2, autoriteit 74%

Ik trek dit niet uit mijn hoofd. Dit is waar ik mee heb moeten werken, zij het vereenvoudigd. Laten we zeggen dat je in principe een programma hebt dat een opsomming nodig heeft:

enum {
   a, b, c;
} myenum;

Maar in plaats daarvan hebben we

HashTable t;
t["a"] = 0;
t["b"] = 1;
t["c"] = 2;

Maar natuurlijk is geen enkele implementatie van een hash-tabel goed genoeg, dus er is een lokale implementatie van hash-tabellen, die ongeveer 10 keer meer code bevat dan een gemiddelde open source-implementatie met de helft van de functies en het dubbele aantal bugs. De HashTable is feitelijk virtueel gedefinieerd en er is een fabriek HashTableFactory om instanties van HashTables te maken, maar trouw aan het patroon is HashTableFactory ook virtueel. Om een oneindige cascade van virtuele klassen te voorkomen is er een functie

HashTableFactory *makeHashTableFactor();

Dus overal waar de code myenum‘s nodig heeft, staat er een verwijzing naar de instantie van een HashTable en HashTableFactory, voor het geval je meer HashTables wilt maken. Maar wacht, dat is niet alles! Dit is niet hoe de hash-tabel wordt geïnitialiseerd, maar het wordt gedaan door een code te schrijven die XML leest:

<enum>
  <item name="a" value="0"/>
  <item name="b" value="1"/>
  <item name="c" value="2"/>
</enum>

en wordt ingevoegd in een hashtabel. Maar de code is “geoptimaliseerd” zodat het geen ascii-bestand myenum.xml leest, maar in plaats daarvan is er een script voor compileren dat het volgende genereert:

const char* myenumXML = [13, 32, 53 ....];

van myenum.xml en de hashtabel wordt geïnitialiseerd door een functie:

void xmlToHashTable(char *xml, HashTable *h, HashTableFactory *f);

die heet:

HashTableFactory *factory = makeHashTableFactory();
HashTable *t = facotry.make();
xmlToHashTable(myenumXML, t, f);

Ok, dus we hebben veel code om een enum-structuur te krijgen. Het wordt eigenlijk gebruikt in een functie:

void printStuff(int c) {
   switch (c) {
   case a: print("a");
   case b: print("b");
   case c: print("c");
   }
}

en dit wordt aangeroepen in een context waarin:

void stuff(char* str) {
   int c = charToEnum(str);
   printStuff(c);
}

Dus wat we eigenlijk hebben is in plaats van

void stuff(char *str) {
   printf(str);
}

we zijn erin geslaagd om duizenden regels code te genereren (privé nieuw, buggy, complex, implementatie van hashtabellen en xml-lezers en schrijver) in plaats van de bovenstaande 3.


Antwoord 3, autoriteit 67%

Er is ook Ravioli-code, wat het tegenovergestelde is. Leuke stukjes functionaliteit, een schone interface netjes gewikkeld rond vlezige goedheid, alles zat in een mooie saus van kader.


Antwoord 4, autoriteit 37%

Van een Linux SCSI-stuurprogramma (dat naamloos blijft om de schuldigen te beschermen):

wait_nomsg:
        if ((inb(tmport) & 0x04) != 0) {
                goto wait_nomsg;
        }
        outb(1, 0x80);
        udelay(100);
        for (n = 0; n < 0x30000; n++) {
                if ((inb(tmport) & 0x80) != 0) {        /* bsy ? */
                        goto wait_io;
                }
        }
        goto TCM_SYNC;
wait_io:
        for (n = 0; n < 0x30000; n++) {
                if ((inb(tmport) & 0x81) == 0x0081) {
                        goto wait_io1;
                }
        }
        goto TCM_SYNC;
wait_io1:
        inb(0x80);
        val |= 0x8003;          /* io,cd,db7  */
        outw(val, tmport);
        inb(0x80);
        val &= 0x00bf;          /* no sel     */
        outw(val, tmport);
        outb(2, 0x80);
TCM_SYNC:
/* ... */
small_id:
        m = 1;
        m <<= k;
        if ((m & assignid_map) == 0) {
                goto G2Q_QUIN;
        }
        if (k > 0) {
                k--;
                goto small_id;
        }
G2Q5:                   /* srch from max acceptable ID#  */
        k = i;                  /* max acceptable ID#            */
G2Q_LP:
        m = 1;
        m <<= k;
        if ((m & assignid_map) == 0) {
                goto G2Q_QUIN;
        }
        if (k > 0) {
                k--;
                goto G2Q_LP;
        }
G2Q_QUIN:               /* k=binID#,       */

Hoe heb ik dit juweeltje gevonden?

find /usr/src/linux -type f -name \*.c | 
while read f
do 
    echo -n "$f "
    sed -n 's/^.*goto *\([^;]*\);.*/\1/p' $f | sort -u | wc -l
done | 
sort +1rn |
head

De uitgang is een reeks lijnen die bestanden vermeldt die zijn besteld door het aantal GOTOS tot verschillende labels, zoals het volgende:

kernel/fork.c 31
fs/namei.c 35
drivers/infiniband/hw/mthca/mthca_main.c 36
fs/cifs/cifssmb.c 45
fs/ntfs/super.c 47

Antwoord 5, Autoriteit 37%

Echte spaghetti-code is gedaan in COBOL en gebruikte de wijziging van het alter.

Hier is een voorbeeld , terwijl ik een “humor” heeft vermeld, ik heb dit soort gezien van ding. Bijna werd eenmaal ontslagen om op te merken dat een programma met een alter-verklaring duidelijk in een staat van zonde was. Ik weigerde om dat programma ‘te handhaven “, het was sneller om het te vervangen dan het begrijpen.


Antwoord 6, Autoriteit 33%

Vergeet niet om het object-georiënteerde spaghetti te vermelden.
Dit is wanneer je alle ontwerppatronen in het boek probeert te gebruiken, zelfs als ze niet logisch zijn. Dit leidt tot spaghetti-code op conceptueel niveau, dat veel schadelijker is voor kwaliteit dan de klassieke goto-gebaseerde spaghetti-code.


Antwoord 7, Autoriteit 30%

U hebt er om gevraagd, u krijgt het:

Dit is de bron van een DOS .COM-bestand dat de blauwe Donau Waltz speelt. Het uitvoerbare bestand is slechts 176 bytes in grootte. Code wordt hergebruikt als gegevens en vice versa.

.286
.model tiny
g4 equ 55-48           ; removed note-decoding !
a4 equ 57-48           ; now: storing midi-notes for octaves 0..2 and convert
h4 equ 59-48           ; to 4..6 with a simple add 48.
c5 equ 60-48
d5 equ 62-48
e5 equ 64-48
g5 equ 67-48
h5 equ 71-48
c6 equ 72-48
d6 equ 74-48
e6 equ 76-48
g6 equ 79-48           ; = 00011111b
pp  equ 0              ;  c4 is not used in the walz, using it as play-pause.
EOM equ 1              ; c#4 is also available... End Of Music
                       ; warning: experts only beyond this point !
pau1 equ 00100000b     ; bitfield definitions for note-compression
pau2 equ 01000000b     ; you can or a pau to each note!
pau3 equ 01100000b
;rep1 equ 01000000b    ; rep1 is history (only used once).
;rep3 equ 11000000b    ; rep3 was never used.
rep2 equ 10000000b     ; or a rep2 to a note to play it 3 times.
drumsize equ 5
.code
org 100h
start:
                mov  ah,9
                mov  dx,offset msg
                int  21h                    ; print our headerstring
                mov  dx,0330h               ; gus midi megaem -port
                mov  si,offset music_code   ; start of music data
mainloop:
    ; get new note (melody)
                xor  bp,bp                  ; bp= repeat-counter
                lodsb                       ; get a new note
                cmp  al, EOM                ; check for end
                jne  continue
                ret
continue:
                jns  no_rep2                ; check for rep2-Bit
                inc  bp
                inc  bp                     ; "build" repeat-counter
no_rep2:
                push ax                     ; save the note for pause
    ; "convert" to midi-note
                and  al,00011111b
                jz   skip_pp                ; check pp, keep it 0
                add  al,48                  ; fix-up oktave
skip_pp:
                xchg ax,bx                  ; bl= midi-note
play_again:
                mov  cl,3
                push cx                     ; patch program (3= piano)
                push 0c8h                   ; program change, channel 9
    ; wait (cx:dx) times
                mov  ah,86h                 ; wait a little bit
                int  15h
    ; prepare drums
                dec  di                     ; get the current drum
                jns  no_drum_underflow
                mov  di,drumsize
no_drum_underflow:
    ; play drum
                push dx                     ; volume drum
                push [word ptr drumtrk+di]  ; note   drum
                mov  al,99h
                push ax                     ; play channel 10
    ; play melody
                push dx                     ; volume melody
                push bx                     ; note   melody
                dec  ax                     ; replaces dec al :)
                push ax                     ; play channel 9
    ; send data to midi-port
                mov  cl,8                   ; we have to send 8 bytes
play_loop:
                pop  ax                     ; get the midi event
                out  dx,al                  ; and send it
                loop play_loop
    ; repeat "bp" times
                dec  bp                     ; repeat the note
                jns  play_again
    ; check and "play" pause
                xor  bx,bx                  ; clear the note, so we can hear
                                            ; a pause
    ; decode pause value
                pop  ax
                test al,01100000b
                jz   mainloop               ; no pause, get next note
    ; decrement pause value and save on stack
                sub  al,20h
                push ax
                jmp  play_again             ; and play next drum
; don't change the order of the following data, it is heavily crosslinked !
music_code      db pp or rep2
                db g4 or rep2 or pau1
                db h4 or pau1, d5 or pau1, d5 or pau3
                db d6 or pau1, d6 or pau3, h5 or pau1, h5 or pau3
                db g4 or rep2 or pau1
                db h4 or pau1, d5 or pau1, d5 or pau3
                db d6 or pau1, d6 or pau3, c6 or pau1, c6 or pau3
                db a4 or rep2 or pau1
                db c5 or pau1, e5 or pau1, e5 or pau3
                db e6 or pau1, e6 or pau3, c6 or pau1, c6 or pau3
                db a4 or rep2 or pau1
                db c5 or pau1, e5 or pau1, e5 or pau3
                db e6 or pau1, e6 or pau3, h5 or pau1, h5 or pau3
                db g4 or rep2 or pau1
                db h4 or pau1, g5 or pau1, g5 or pau3
                db g6 or pau1, g6 or pau3, d6 or pau1, d6 or pau3
                db g4 or rep2 or pau1
                db h4 or pau1, g5 or pau1, g5 or pau3
                db g6 or pau1, g6 or pau3, e6 or pau1, e6 or pau3
                db a4 or rep2 or pau1
                db c5 or pau1, e5 or pau1, e5 or pau3, pp or pau3
                db c5 or pau1, e5 or pau1, h5 or pau3, pp or pau3, d5 or pau1
                db h4 or pau1, h4 or pau3
                db a4 or pau1, e5 or pau3
                db d5 or pau1, g4 or pau2
;                db g4 or rep1 or pau1
; replace this last "rep1"-note with two (equal-sounding) notes
                db g4
                db g4 or pau1
msg             db EOM, 'Docking Station',10,'doj&sub'
drumtrk         db 36, 42, 38, 42, 38, 59  ; reversed order to save some bytes !
end start

Antwoord 8, autoriteit 26%

Echtespaghetti-code vereist een groot aantal niet-lokale goto’s. Helaas is dit niet mogelijk met de meeste moderne talen.

Bewerken: sommigen suggereren uitzonderingen en longjmp als vervanging voor GOTO. Maar deze zijn veelte beperkt en gestructureerd, aangezien ze je alleen toestaan om terug te keren in de callstack. Met Real GOTO kun je naar elkeregel overalin het programma springen, wat nodig is om echte spaghetti te maken.


Antwoord 9, autoriteit 26%

In eenvoudige bewoordingen is spaghetticode elke code in welke programmeertaal dan ook waarin het niet mogelijk is om de volgende post van uitvoering te traceren, of op zijn minst moeilijk te bepalen waar het volgende punt naartoe gaat als reactie op één actie.


Antwoord 10, autoriteit 15%

Dit is van een MIDI-parser die ik enige tijd geleden heb geschreven. Het was een snelle en vuile proof of concept, maar toch zal ik de schuld op zich nemen voor zijn lelijkheid: 4 niveaus van geneste conditionals plus de gevreesde meervoudige returns. Deze code was bedoeld om 2 MIDI-events te vergelijken om ze op prioriteit te sorteren bij het schrijven naar een bestand. Hoe lelijk het ook was, het deed het werk behoorlijk.

internal class EventContainerComparer : IComparer {
    int IComparer.Compare(object a, object b) {
        MIDIEventContainer evt1 = (MIDIEventContainer) a;
        MIDIEventContainer evt2 = (MIDIEventContainer) b;
        ChannelEvent chanEvt1;
        ChannelEvent chanEvt2;
        if (evt1.AbsoluteTime < evt2.AbsoluteTime) {
            return -1;
        } else if (evt1.AbsoluteTime > evt2.AbsoluteTime) {
            return 1;
        } else {    
            // a iguar valor de AbsoluteTime, los channelEvent tienen prioridad
            if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is MetaEvent) {
                return -1;
            } else if(evt1.MidiEvent is MetaEvent && evt2.MidiEvent is ChannelEvent){
                return 1;
            //  si ambos son channelEvent, dar prioridad a NoteOn == 0 sobre NoteOn > 0
            } else if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is ChannelEvent) {
                chanEvt1 = (ChannelEvent) evt1.MidiEvent;
                chanEvt2 = (ChannelEvent) evt2.MidiEvent;
                // si ambos son NoteOn
                if( chanEvt1.EventType == ChannelEventType.NoteOn 
                    && chanEvt2.EventType == ChannelEventType.NoteOn){
                    //  chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
                    if(chanEvt1.Arg1 == 0 && chanEvt2.Arg1 > 0) {
                        return -1;
                    //  chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
                    } else if(chanEvt2.Arg1 == 0 && chanEvt1.Arg1 > 0) {
                        return 1;
                    } else {
                        return 0;
                    }
                // son 2 ChannelEvent, pero no son los 2 NoteOn, el orden es indistinto
                } else {
                    return 0;
                }
            //  son 2 MetaEvent, el orden es indistinto
            } else {
                return 0;
            }
        }
    }
}

Antwoord 11, autoriteit 11%

Hier is het Duff’s Device, van Matts antwoord op deze vraag:

int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7:      *to = *from++;
case 6:      *to = *from++;
case 5:      *to = *from++;
case 4:      *to = *from++;
case 3:      *to = *from++;
case 2:      *to = *from++;
case 1:      *to = *from++;
           } while (--n > 0);
}

Antwoord 12

Spaghetticode: ontstaan in de vroege jaren 60 in Italië als alternatief recept voor bepaalde pastagerechten, werd spaghetticode bedacht door een restaurantondernemer die probeerde de creatie van een onfeilbaar voorgerecht te automatiseren. Gedrukt door het gebrek aan tijd om het ontwerp te voltooien, sneed de ingenieur / chef-kok de bochten af die al vroeg problemen in het recept introduceerden. In een verwoede poging om een slecht idee te verhelpen, werden er snel verschillende kruiden aan het brouwsel toegevoegd toen het recept uit de hand liep. Het resultaat was een draderige, kronkelige, maar potentieel smakelijke stapel tekst die later zou uitgroeien tot een praktijk die wordt gekoesterd door ontwikkelaars over de hele wereld.


Antwoord 13

Heb je ooit gekeken naar code gegenereerd door Flex/Bison scanner en generator? Een overvloed aan labels en preprocessor-richtlijnen.

Het is absoluut onmogelijk om te begrijpen wat erin zit.. en absoluut onmogelijk om de stroom van het programma te volgen.

Dat is beslist spaghetti-code.

Other episodes