2016-06-27 20 views
3

Ich schaute auf einige C++ - Netzwerkcode. Als ich ging, um herauszufinden, wie groß der Puffer war, fand ich etwas wie folgt aus:Verwenden Sie bitweise oder anstelle von std :: max für die Puffergröße

static const uint32_t MAX_COMMAND_BUFFER = 
     sizeof(struct S1) | sizeof(struct S2) | sizeof(struct S3) | 
     sizeof(struct S4) | sizeof(struct S5) | sizeof(struct S6); 

Ich glaube, hier die Grundidee ist, dass der Puffer eine dieser 6 structs enthalten. Dies ist in einem äußeren Bereich deklariert, so dass es für Dinge wie einen späteren äußeren Umfang uint8_t Array-Größe verfügbar ist.

Jetzt normalerweise würde ich erwarten, etwas zu sehen, entweder mit der std :: max() -Funktion, oder der tertiary-Operator, um die genaue Größe der größten dieser 6 Strukturen zu berechnen. Ich habe die obige Redewendung nie zuvor gesehen und musste aufhören, mich selbst zu überzeugen, dass es funktionieren würde.

Da ich also neu bin, möchte ich im Allgemeinen wissen, wie gültig das ist und welche Vor- und Nachteile es hat.

Was kann ich sehen, ist -

pro:

  • Es sollten Sie immer mindestens so groß wie Ihre größte struct Größe eine Größe geben. Was ist alles, was Sie wirklich brauchen, wenn der Platz nicht zu hoch ist?
  • Es scheint zur Kompilierzeit kalkulierbar zu sein.
  • Es sollte sogar mit sehr alten C++ (oder sogar C) Compiler funktionieren.

con:

  • Es kann die Größe fast doppelt so groß wie nötig machen.
  • Es macht es viel schwieriger für einen Menschen offline herauszufinden, genau wie groß der Puffer ist.
  • Es ist seltsam/unerwartet.
+1

Je nachdem, für welchen Compiler der Code geschrieben wurde, könnte es sich um eine Optimierung gehandelt haben, zumal der Compiler (wie Sie gesagt haben) das Ganze zur Kompilierzeit berechnen kann. – refi64

Antwort

2

Schreiben Sie einfach die Absicht, nach unten:

#include <algorithm> 
struct S1 {}; 
struct S2 {}; 
struct S3 {}; 
int main() { 
    static const unsigned maximum = std::max({sizeof(S1), sizeof(S2), sizeof(S3) /*, ... */}); 
} 

Es gibt keine 'pro' Dinge in Verschleiern.

+0

Definitiv stimme mit dem letzten Satz überein, und ich schätze die Ideen für das "Festsetzen" (da ich das tun kann). Ich bin jedoch besonders interessiert daran, ob dies eine bekannte Redewendung ist, die ich meine gesamte 31-jährige Karriere irgendwie verpasst habe, und wenn nicht, habe ich ein gutes Händchen für die Vor-/Nachteile, damit ich mich intelligent entscheiden kann (und unterstütze meine Entscheidung). –

3

Es ist gültig in dem Sinne, dass MAX_COMMAND_BUFFER wird immer mindestens so groß wie das Maximum aller Größen dieser Strukturen sein. Es ist einfach, dies zu überprüfen - bitweise | - Wenn Sie alle Größen zusammen verwenden, erhalten Sie einen Wert, der alle die gleichen Bits wie die größte Größe hat, plus möglicherweise einige andere.

Allerdings ist es sehr verwirrend und Sie müssen aufhören und darüber nachdenken. Plus, wenn Sie zwei Größen haben, die 8 und 7 sind, werden Sie mit 15 enden, was wahrscheinlich nicht das ist, was Sie wollen. Zum Glück hat std::max eine constexpr Überlastung, die ein initializer_list<T> nimmt, so dass nur verwenden:

static constexpr size_t MAX_COMMAND_BUFFER = 
//    ^^^^^^ 
     std::max({sizeof(struct S1), sizeof(struct S2), ..., sizeof(struct S6)}); 

Wenn Ihr Compiler nicht, dass die Überlastung nicht unterstützt, es ist ziemlich einfach, eine variadische Funktion Vorlage zu schreiben, um das Gleiche zu erreichen.

+0

Können Sie eine C++ - Standardversion für diese Alternative verwenden? Wir müssen einige ältere (zB: C++ 11) Compiler-Versionen unterstützen, und ich vermute, dass wir im allgemeinen Fall nicht die einzigen sind. –

0

Das sieht ziemlich hokey aus. Ich würde sizeof(union { S1 s1; S2 s2; S3 s3; S4 s4; S5 s5; S6 s6; }) verwenden.(nicht getestet)

2

Ich stimme Ihnen zu, ich denke, die Art, wie sie es gemacht haben, ist funky und suboptimal.

Die Art und Weise habe ich diese Art der Sache zu tun war, eine Vereinigung aller Strukturen zu machen, und dann die Größe der Vereinigung verwenden ...

union Commands { 
    S1 s1; 
    S2 s2; 
    ... 
    S6 s6; 
}; 

sizeof(Commands); 

Und wenn Sie kombinieren diese mit einer Struktur halten Sie Ihre Paket-Header, die durch die Vereinigung gefolgt ...

struct Packet { 
    Header header; 
    Commands command; 
}; 

Sie einen Zeiger auf Ihre Puffer einstellen können, und haben einen einfachen Zugriff auf alle Ihre Daten ...

Packet *packet = (Packet)&buf[0]; 
switch(packet->header.command_type) { 
    case COMMAND_DO_STUFF: 
    printf("%d\n", packet->command.s1.stuff); 
    break; 
    case COMMAND_QUIT: 
    .. 
    break; 
};