12 Stimmen

C++ Beispiel für Coding Horror oder brillante Idee?

Bei einem früheren Arbeitgeber schrieben wir Binärnachrichten, die "über den Draht" an andere Computer geschickt werden mussten. Jede Nachricht hatte einen Standard-Header, etwa so:

class Header
{
    int type;
    int payloadLength;
};

Alle Daten waren zusammenhängend (Kopfzeile, unmittelbar gefolgt von den Daten). Wir wollten an die Nutzdaten gelangen, da wir einen Zeiger auf einen Header hatten. Traditionell könnte man etwas sagen wie:

char* Header::GetPayload()
{
    return ((char*) &payloadLength) + sizeof(payloadLength);
}

oder sogar:

char* Header::GetPayload()
{
    return ((char*) this) + sizeof(Header);
}

Das schien mir etwas zu langatmig, also habe ich mir etwas ausgedacht:

char* Header::GetPayload()
{
    return (char*) &this[1];
}

Auf den ersten Blick wirkt es eher beunruhigend, vielleicht zu seltsam für den Gebrauch - aber sehr kompakt. Es gab eine Menge Debatten darüber, ob es brillant oder eine Abscheulichkeit ist.

Also, was ist es - Verbrechen gegen die Codierung oder eine gute Lösung? Hatten Sie jemals einen ähnlichen Kompromiss?

-Update:

Wir haben das Array mit der Größe Null ausprobiert, aber damals gaben die Compiler Warnungen aus. Wir sind schließlich zur inhärenten Technik übergegangen: Message leitet sich von Header ab. Das funktioniert in der Praxis gut, aber im Prinzip sagen Sie, dass eine Nachricht ein Header ist - was ein wenig umständlich ist.

4voto

Jan de Vos Punkte 3650

Haben Sie den Trick mit dem "leeren Array-Mitglied" in Betracht gezogen? Ich erinnere mich, es oft zu sehen, und sogar mit ihm ein oder zweimal, aber ich kann nicht scheinen, um alle wirklich gute Referenzen zu finden (außer, vielleicht, die eine unten referenziert).

Der Trick besteht darin, dass Sie Ihre Struktur als

struct bla {
    int i;
    int j;
    char data[0];
}

Dann zeigt das "data"-Mitglied einfach auf das, was sich hinter den Kopfzeilen befindet. Ich bin nicht sicher, wie portabel dies ist; Ich habe es mit '1' als die Array-Größe als gut gesehen.

(Die Verwendung der nachstehenden URL als Referenz unter Verwendung der Syntax '[1]' scheint nicht zu funktionieren, da sie zu lang ist. Hier ist der Link:)

http://developer.apple.com/documentation/DeveloperTools/gcc-4.0.1/gcc/Zero-Length.html

3voto

Kluge Punkte 3447

Wenn es funktioniert - und zwar durchgängig - dann ist es eine elegante Lösung.

Normalerweise funktioniert es im Speicher, da der Compiler mit Ausrichtungsproblemen fertig wird und Sie davon ausgehen können, dass die Nutzlast dem Header im korrekt ausgerichteten Speicherbereich folgt.

Ich könnte mir vorstellen, dass dies nicht funktioniert, wenn die Header/Payload-Objekte "über die Leitung" gestreamt werden, da der von Ihnen verwendete Streaming-Mechanismus sich wahrscheinlich nicht um die Ausrichtung der Objekte an einer bestimmten Grenze kümmert. Daher kann die Nutzlast direkt auf den Header folgen, ohne dass es zu einer bestimmten Ausrichtung kommt.

Um es mit einer Redewendung zu sagen: Elegant ist, was elegant macht. Es ist also elegant, solange man darauf achtet, wie man es streamt.

1voto

QBziZ Punkte 3006

Für mich ist das im Grunde das Gleiche. Beides sind Formen des Byte-Jonglierens, was immer riskant, aber nicht unmöglich ist, es richtig zu machen. Die erste Form ist ein bisschen akzeptierter und erkennbarer. Ich persönlich würde schreiben :

char* Header::GetPayload()
{
    return ((char*) this) + sizeof(*this);
}

1voto

spoulson Punkte 20898

Vergessen Sie nicht, dass VC++ möglicherweise ein Padding für die sizeof() Wert auf die Klasse. Da das angegebene Beispiel voraussichtlich 8 Byte groß ist, ist es automatisch DWORD-ausgerichtet und sollte daher in Ordnung sein. Prüfen Sie #pragma pack .

Allerdings stimme ich zu, dass die angeführten Beispiele einen gewissen Grad an Coding Horror darstellen. Viele Win32-Datenstrukturen enthalten einen Zeiger-Platzhalter in der Header-Struktur, wenn Daten variabler Länge folgen. Dies ist wahrscheinlich der einfachste Weg, um diese Daten zu referenzieren, sobald sie in den Speicher geladen sind. Die MAPI SRowSet Struktur ist ein Beispiel für diesen Ansatz.

1voto

Raindog Punkte 1418

Ich mache etwas Ähnliches, und fast jedes MMO oder Online-Videospiel, das jemals geschrieben wurde, tut das auch. Allerdings haben sie ein Konzept, das "Paket" genannt wird, und jedes Paket hat sein eigenes Layout. Sie könnten also Folgendes haben:

struct header
{
    short id;
    short size;
}

struct foo
{
    header hd;
    short hit_points;
}

short get_foo_data(char *packet)
{
    return reinterpret_cast<foo*>(packet)->hit_points;
}

void handle_packet(char *packet)
{
    header *hd = reinterpret_cast<header*>(packet);
    switch(hd->id)
    {
        case FOO_PACKET_ID:
            short val = get_foo_data(packet);
        //snip
    }
}

Und das tun sie für die meisten ihrer Pakete. Einige Pakete haben offensichtlich dynamische Größen, und für diese Mitglieder verwenden sie Felder mit Längenvorgaben und eine gewisse Logik, um die Daten zu analysieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X