2017-12-23 10 views
1

Für viele Zwecke sind kurze Strings/Char-Arrays, die in eine vorzeichenlose 32-Bit-Ganzzahl gepackt sind, ziemlich nützlich, da sie auf einen Schlag mit einem einfachen Ganzzahlvergleich verglichen und in switch-Anweisungen verwendet werden können Lesbarkeit.Was ist der effizienteste Weg, um eine kurze Zeichenfolge in eine 32-Bit-Ganzzahl zu konvertieren?

Der häufigste Weg, um diese kurzen Strings zu 32-Bit-Integer zu konvertieren ist zu verschieben/oder:

#include <stdint.h> 

uint32_t quadchar(const char* _str) 
{ 

    uint32_t result = 0; 

    for(size_t i=0; i<4; i++) 
    { 
     if(_str[i] == 0) 
      return result; 
     result = (result << 8) | _str[i]; 
    } 

    return result; 
} 

Strings, die zu lang sind, wird abgeschnitten.

So weit so gut, aber das muss zur Laufzeit gemacht werden, was ein bisschen Zeit kostet. Wäre es auch möglich, dies zur Kompilierzeit zu tun?

+2

Mit C++ 14 und später müssen Sie nur noch "constexpr" zu Ihrer Funktion hinzufügen. Es gibt bereits eine Antwort, die zeigt, wie dies (nämlich rekursiv) für C++ 11 zu tun ist. Für C++ 03 ist es praktisch unmöglich, eine gute Syntax zu erhalten, aber eine C++ 03-Lösung ist die Quellcode-Vorverarbeitung über ein Skript. –

+0

Die Union-Methode wäre ein undefiniertes Verhalten, und das Ergebnis hängt von der Endianess der CPU ab. – Barmar

+0

@Barmar: wahr, thx. Ich habe diese Passage entfernt. – user2328447

Antwort

0

Ab C++ 11 ist es möglich, dies zur Kompilierungszeit ohne Kosten zur Laufzeit unter Verwendung der constexpr specifier zu tun.

namespace Internal 
{ 
    uint32_t inline constexpr quadchar(char const *_input, 
     uint8_t _idx, uint32_t _result) 
    { 
     return _idx == 4 ? _result 
      : *_input ? quadchar (_input+1, _idx + 1, (_result << 8) | *_input) 
       : _result; 
    } 
} 

uint32_t inline constexpr quadchar(char const *_input) { 
    return Internal::quadchar(_input, 0, 0); 
} 

Ich habe die Implementierungsüberladung in einen internen Namespace platziert, um sie vor dem Benutzer zu verbergen. Die Syntax ist nicht so gut wie im obigen Laufzeitbeispiel, da Sie if in constexpr nicht verwenden können, aber ich denke, es ist es wert.

+0

Die gleiche Portabilitätswarnung gilt für diese Antwort.Es kann sein, dass es egal ist, wenn die konvertierte Zeichenkette niemals persistiert oder über das Netzwerk gesendet wird. Ein Anruf zu 'honl()' mag hier vernünftig sein. Bei vielen CPUs wird in sehr wenigen Anweisungen kompiliert - möglicherweise nur eine. – marko

+2

Sorry, ich verstehe nicht genau was du meinst. Was ist hier nicht tragbar? Soweit ich sehen kann, sollte alles tragbar sein, und nicht Endianess-abhängig. Und was ist 'honl()', bitte? Konnte nichts darüber finden. – user2328447

1

Es ist keine detaillierte Hilfsfunktion erforderlich: Sie können Standardwerte verwenden.

Und es gibt keine Notwendigkeit für eine Doppel ternären Operator: Sie alle mit einem einzigen Test machen

std::uint32_t inline constexpr quadchar (char const * input, 
             std::size_t idx = 0U, 
             std::uint32_t result = 0U) 
{ 
    return (idx < 4U) && *input 
     ? quadchar(input+1, idx+1U, (result << 8) | *input) 
     : result; 
} 

Aber es ist ein wenig mehr tragbar und Generika zu machen, schlage ich vor,

1) verwenden sizeof() statt 4 für die idx Grenze

2) verwenden CHAR_BIT statt 8 für die result Verschiebung (remember "enthalten")

3) Verwenden Sie einen Vorlagetyp (standardmäßig std::uint32_t, wenn Sie möchten) für den Typ result.

So etwas wie

template <typename I = std::uint32_t> 
constexpr inline I ichar (char const * input, 
          I result = 0U, 
          std::size_t idx = 0U) 
{ 
    return (idx < sizeof(I)) && *input 
     ? ichar(input+1, idx+1U, (result << CHAR_BIT) | *input) 
     : result; 
} 

, die Sie

constexpr auto u32 = ichar(ptr); 

aufrufen können, wenn Sie einen std::uint32_t wollen, oder (durch Beispiel)

constexpr auto u64 = ichar<std::uint64_t>(ptr); 

für andere zurück Typen.

+0

Danke. Die 'sizeof()' - und Template-Ideen sind sehr gut. Ich bin mir nicht so sicher über 'CHAR_BIT'. Falls 'CHAR_BIT'! = 8 ist, wird sich das Ergebnis aufgrund der unterschiedlichen Verschiebung von dem eines 8-Bit-Zeichens unterscheiden; Ich denke, das ist weniger portabel als mehr. Es wäre vielleicht besser, stattdessen ein "& 0xff" mit dem Eingabe-Zeichen zu machen. Die Hilfsfunktion wurde implementiert, um die Parameter "result" und "input" zu verbergen, weil sie für den Benutzer keinen Sinn ergeben. Aber selbstverständlich sind Standardparameter auch in Ordnung. – user2328447

+0

@ user2328447 - Ich habe auch Zweifel an 'CHAR_BIT' (zB: existiere' std :: uint32_t' in einer Plattform mit 'CHAR_BIT = 12'?), Aber nimm an, dass wenn' CHAR_BIT> 8' ('8 'ist das Minimum), jedes char in' input' ist mit 'CHAR_BIT' Bits; also mit '(Ergebnis << 8) | * input_ Sie haben eine Kollision (or-bitted) der letzten Bits in vorhergehenden char und die erste in '* input'. – max66

+0

@ user2328447 - und ja: die '& 0xff'-Idee könnte eine gute alternative Lösung sein; Nehmen Sie an, dass Sie auf diese Weise die Verwendung einiger Bits in Zeichen (im Fall von CHAR_BITS> 8) verlieren und die richtigen Bits in Eingabe-Zeichen verwalten, was schwierig und plattformabhängig sein könnte. – max66

Verwandte Themen