Ich habe an einigen Stellen gelesen, dass es mit den neuen String-Literalen von C ++ 11 möglich sein könnte, den Hash eines Strings zur Kompilierungszeit zu berechnen. Es scheint jedoch niemand bereit zu sein, herauszukommen und zu sagen, dass es möglich sein wird oder wie es gemacht werden würde.
- Ist das möglich?
- Wie würde der Bediener aussehen?
Ich interessiere mich besonders für solche Anwendungsfälle.
void foo( const std::string& value )
{
switch( std::hash(value) )
{
case "one"_hash: one(); break;
case "two"_hash: two(); break;
/*many more cases*/
default: other(); break;
}
}
Hinweis: Die Hash-Funktion zur Kompilierungszeit muss nicht genau so aussehen, wie ich sie geschrieben habe. Ich habe mein Bestes getan, um zu erraten, wie die endgültige Lösung aussehen würde, meta_hash<"string"_meta>::value
könnte aber auch eine praktikable Lösung sein.
c++
metaprogramming
c++11
hash
deft_code
quelle
quelle
Antworten:
Dies ist etwas spät, aber es ist mir gelungen, eine CRC32-Funktion zur Kompilierungszeit mithilfe von zu implementieren
constexpr
. Das Problem dabei ist, dass es zum Zeitpunkt des Schreibens nur mit GCC und nicht mit MSVC oder Intel Compiler funktioniert.Hier ist das Code-Snippet:
CrcVal01
ist gleich 0x335CC04AHoffe das wird dir helfen!
quelle
constexpr
ist nicht verfügbar in VS2013, außer im November 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/…Zumindest durch meine Lektüre von §7.1.5 / 3 und §5.19 könnte Folgendes legitim sein:
Dies scheint den Grundregeln in §7.1.5 / 3 zu folgen:
Es ist fraglich, ob es sich bei den
*input
s um eine illegale Umwandlung von Wert zu Wert handelt, und ich bin mir nicht sicher, ob ich die Regeln in §5.19 / 2/6/2 1 verstehe und §4.1 gut genug verstehe, um sicher zu sein.Aus praktischer Sicht wird dieser Code von (zum Beispiel) g ++ akzeptiert, zumindest bis zu g ++ 4.7.1.
Verwendung wäre so etwas wie:
Um die Anforderungen von §5.19 / 2/6/2 zu erfüllen, müssen Sie möglicherweise Folgendes tun:
quelle
constexpr
. 2: Sie haben keine Haltebedingung (wo*input == nullptr
) und wie ich versteheconstexpr
, können Sie keine haben.(unsigned)-1
falls vorhanden. und gibt 1 für alle anderen Zeichenfolgen zurück. Mit ternärem bedingtem Operator umschreiben?Dies ist ein Versuch, das Problem des OP so genau wie möglich zu lösen.
Live-Beispiel .
Beachten Sie die Hauptunterschied -
std::hash
nicht verwendet werden können, wie wir die Kontrolle über nicht habenstd::hash
s - Algorithmus‘, und wir müssen es als neu implementieren ,constexpr
um es bei der Kompilierung zu bewerten. Außerdem gibt es keine "transparenten" Hashesstd
, sodass Sie (ohne Erstellen einesstd::string
) keinen Rohzeichenpuffer alsstd::string
.Ich habe den
std::string
benutzerdefinierten Hasher (mit transparenterconst char*
Unterstützung) in einenmy_hash
Namespace gesteckt , damit Sie ihn in einem speichern können,std::unordered_map
wenn Sie Konsistenz benötigen.Basierend auf der exzellenten Antwort von @ JerryCoffin und dem Kommentarthread darunter, aber mit dem Versuch, sie mit den aktuellen C ++ 11-Best Practices zu schreiben (anstatt sie vorwegzunehmen!).
Beachten Sie, dass die Verwendung eines "Raw-Hash" für eine
switch
Anweisungcase
gefährlich ist. Anschließend sollten Sie einen==
Vergleich durchführen, um zu bestätigen, dass er funktioniert hat.quelle
Dieser Ausschnitt basiert auf dem von Clement JACOB. Funktioniert aber auch mit Clang. Und es sollte beim Kompilieren schneller sein (es gibt nur einen rekursiven Aufruf, nicht zwei wie im ursprünglichen Beitrag).
Siehe Proof of Concept hier
quelle
Beachten Sie, dass das hier gezeigte Formular nicht in den Standard aufgenommen wurde, wie unten angegeben.
Es wird vermutet, dass die Verarbeitung von Zeichenfolgen für die Kompilierungszeit durch benutzerdefinierte Literale möglich wird, die in N2765 vorgeschlagen werden .
Wie ich bereits erwähnt habe, kenne ich keinen Compiler, der es derzeit implementiert, und ohne Compiler-Unterstützung kann es nur Vermutungen geben.
In §2.13.7.3 und 4 des Entwurfs haben wir Folgendes:
Kombinieren Sie das mit
constexpr
und wir sollten die Verarbeitung von Kompilierungszeitzeichenfolgen haben.Update: Ich habe übersehen, dass ich den falschen Absatz gelesen habe. Dieses Formular ist für benutzerdefinierte Ganzzahl-Literale und Floating-Literale zulässig, aber anscheinend nicht für String-Literale (§2.13.7.5).
Dieser Teil des Vorschlags scheint nicht angenommen worden zu sein.
Abgesehen davon könnte es mit meinem begrenzten Blick auf C ++ 0x ungefähr so aussehen (ich habe höchstwahrscheinlich etwas falsch gemacht):
Wenn Jerrys Ansatz funktioniert, sollte jedoch Folgendes funktionieren:
quelle
constexpr
benutzerdefiniertem Literal. Ich bin nicht sicher, ob Sie ein Zeichenfolgenliteral als Vorlagenparameter verwenden können. Haben sie keine statische Verknüpfung? (Sie funktionieren zumindest in C ++ 98 und sind daher als Vorlagenparameter verboten).operator ""_hash
funktioniert für mich in Xcode 5.0.2.Eine andere Lösung, die auf der von Clement JACOB basiert und C ++ 11 constexpr (nicht das erweiterte C ++ 14) verwendet, aber nur eine Rekursion aufweist.
Eine Erklärung
combine_crc32
können wir das Ergebnis einer Rekursion unter einer Variablen speichernpart
und zweimal verwenden. Diese Funktion ist eine Problemumgehung für C ++ 11-Grenzwerte, bei denen lokale Variablendeklarationen nicht zulässig sind.ctcrc32
Funktion erwartet ein String-Literal, das als übergeben wirdconst char (&)[len]
. Auf diese Weise können wir die Zeichenfolgenlänge als Vorlagenparameter abrufen und müssen uns nicht auf Makros verlassen.quelle
Das Folgende funktioniert in GCC 4.6.1, und Sie können entweder
hash
oderpack
in Schalterblöcken verwenden.GCC erlaubt anscheinend (?) Keine rekursiven Aufrufe, bei denen wir
s+1
mits
einem Zeiger weiterleiten, weshalb ich dieoff
Variable verwende.quelle
Wenn Sie einen c ++ 17-Compiler und string_view haben, wird dies trivial. Schreiben Sie einfach die normale Version:
quelle
crc32("mystring")
(normalerweise tendiert VS dazu). Der Trick, um dieses Problem zu umgehen, besteht darin, eine constexpr-Variable zu erstellen, die von der Bewertung der Kompilierungszeit Ihres crc32 abhängt. Normalerweiseconstexpr uint32_t val = crc32("mystring");
Hier ist eine weitere C ++ 11-Implementierung (basierend auf der Antwort von @ CygnusX1), die sowohl mit constexpr char-Arrays als auch mit Laufzeitzeichenfolgen funktioniert:
Sie müssen,
str.size() + 1
weillen
in der zweiten Überladungstrlen(str) + 1
auf das Nullzeichen am Ende zurückzuführen ist.Ich habe keine Überladung hinzugefügt,
const char *
da dies die zweite Überladung durcheinander bringt. Sie können problemlos Überladungen fürconst char *, size_t
oder hinzufügenstd::string_view
.quelle
Das ist eine schöne Frage.
Basierend auf der Antwort von Jerry Coffin habe ich eine weitere erstellt, die mit dem std :: hash von Visual Studio 2017 kompatibel ist.
https://github.com/manuelgustavo/cx_hash
quelle
Mir fehlte noch eine crc32-Literal-Variante (was mit Vorlagen nicht möglich ist), daher hier mein Vorschlag, der auf CygnusX1 basiert . Haben einige Tests durchgeführt, zögern Sie nicht, Feedback zu geben.
Testet auf MSVC.
PS: Ich hasse es, woanders nach zusätzlichen Dingen zu suchen, deshalb habe ich die CRC-Tabelle am Ende meiner Antwort kopiert.
Alternative mit Algorithmus von Dan Bernstein (djb2) (kombinierte Antworten von Jerry Coffin + Georg Fritzsche )
Crc32-Tabelle:
quelle