Das Erstellen und Bearbeiten von Zeichenfolgen während der Kompilierungszeit in C ++ bietet mehrere nützliche Anwendungen. Obwohl es möglich ist, Zeichenfolgen zur Kompilierungszeit in C ++ zu erstellen, ist der Prozess sehr umständlich, da die Zeichenfolge als variable Zeichenfolge deklariert werden muss, z
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Operationen wie die Verkettung von Zeichenfolgen, das Extrahieren von Teilzeichenfolgen und viele andere können einfach als Operationen für Zeichenfolgen implementiert werden. Ist es möglich, Zeichenfolgen zur Kompilierungszeit bequemer zu deklarieren? Wenn nicht, gibt es einen Vorschlag in den Arbeiten, der eine bequeme Deklaration von Zeichenfolgen zur Kompilierungszeit ermöglichen würde?
Warum bestehende Ansätze fehlschlagen
Im Idealfall möchten wir in der Lage sein, Zeichenfolgen zur Kompilierungszeit wie folgt zu deklarieren:
// Approach 1
using str1 = sequence<"Hello, world!">;
oder unter Verwendung benutzerdefinierter Literale
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
wo decltype(str2)
hätte ein constexpr
Konstruktor. Eine chaotischere Version von Ansatz 1 kann implementiert werden, wobei die Tatsache ausgenutzt wird, dass Sie Folgendes tun können:
template <unsigned Size, const char Array[Size]>
struct foo;
Das Array müsste jedoch über eine externe Verknüpfung verfügen. Damit Ansatz 1 funktioniert, müssten wir Folgendes schreiben:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
Das ist natürlich sehr unpraktisch. Ansatz 2 ist eigentlich nicht umsetzbar. Wenn wir einen ( constexpr
) Literaloperator deklarieren würden, wie würden wir dann den Rückgabetyp angeben? Da der Operator eine variable Folge von Zeichen zurückgeben muss, müssten wir den const char*
Parameter verwenden, um den Rückgabetyp anzugeben:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Dies führt zu einem Kompilierungsfehler, da s
es sich nicht um einen handelt constexpr
. Der Versuch, dies mit den folgenden Schritten zu umgehen, hilft nicht viel.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
Der Standard schreibt vor, dass diese spezielle Literaloperatorform für Ganzzahl- und Gleitkommatypen reserviert ist. Während 123_s
würde funktionieren, abc_s
würde nicht. Was ist, wenn wir benutzerdefinierte Literale ganz weglassen und nur eine reguläre constexpr
Funktion verwenden?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Nach wie vor stoßen wir auf das Problem, dass das Array, jetzt ein Parameter für die constexpr
Funktion, selbst kein constexpr
Typ mehr ist .
Ich glaube, es sollte möglich sein, ein C-Präprozessor-Makro zu definieren, das eine Zeichenfolge und die Größe der Zeichenfolge als Argumente verwendet und eine Sequenz zurückgibt, die aus den Zeichen in der Zeichenfolge besteht (using BOOST_PP_FOR
, stringification, array subscripts und dergleichen). Ich habe jedoch nicht die Zeit (oder nicht genug Interesse), um ein solches Makro zu implementieren =)
quelle
constexpr
typisierte Vorlagenargumente unterteilt sind, können Sie sie in Funktionen verwenden und Arrays (daher concat, substr usw.) initialisieren.constexpr
Zeichenfolgen können während der Kompilierungszeit analysiert werden, sodass Sie je nach Ergebnis unterschiedliche Codepfade verwenden können. Im Wesentlichen können Sie EDLs in C ++ erstellen. Die Anwendungen sind ziemlich grenzenlos.Antworten:
Ich habe nichts gesehen, was der Eleganz von Scott Schurr
str_const
auf der C ++ Now 2012 entspricht . Es erfordertconstexpr
jedoch.So können Sie es verwenden und was es kann:
Es wird nicht viel cooler als die Überprüfung der Kompilierungszeit!
Sowohl die Verwendung als auch die Implementierung sind frei von Makros. Und es gibt keine künstliche Begrenzung für die Saitengröße. Ich würde die Implementierung hier veröffentlichen, aber ich respektiere Scotts implizites Urheberrecht. Die Implementierung befindet sich auf einer einzelnen Folie seiner Präsentation, die oben verlinkt ist.
quelle
str_const
und die andere basierend aufsequence
). Der Benutzer würdestr_const
die Zeichenfolge initialisieren, aber nachfolgende Vorgänge, die neue Zeichenfolgen erstellen, würdensequence
Objekte zurückgeben.template<char... cs>
. Theoretisch könnten Sie etwas erstellen, das eine Literalzeichenfolge verwendet und den Inhalt zu einer Funktion kompiliert. Siehe die Antwort von dyp. Eine sehr vollständig aussehende Bibliothek ist metaparse . Im Wesentlichen können Sie jede Zuordnung von Literalzeichenfolgen zu Typen definieren und mit dieser Technologie implementieren.constexpr operator==
. Es tut uns leid. Scotts Präsentation soll Ihnen den Einstieg erleichtern. In C ++ 14 ist dies viel einfacher als in C ++ 11. Ich würde nicht einmal die Mühe machen, es in C ++ 11 zu versuchen. Sehen Sie Scotts neuesteconstexpr
Vorträge hier: youtube.com/user/CppConEs ist möglich, dies ohne Boost zu implementieren, indem ein sehr einfaches Makro und einige der C ++ 11-Funktionen verwendet werden:
(Die beiden letzteren sind hier nicht unbedingt erforderlich)
Wir müssen in der Lage sein, eine variadische Vorlage mit vom Benutzer angegebenen Angaben von 0 bis N zu instanziieren - ein Tool, das auch nützlich ist, um beispielsweise das Tupel in das Argument der variadischen Vorlagenfunktion zu erweitern (siehe Fragen: Wie erweitere ich ein Tupel in die Argumente der variadischen Vorlagenfunktion?
" Auspacken "eines Tupels zum Aufrufen eines passenden Funktionszeigers )
Definieren Sie dann eine variable Vorlage namens string mit dem Parameter char vom Typ non:
jetzt der interessanteste Teil - Zeichenliterale in Zeichenfolgenvorlage zu übergeben:
Eine einfache Verkettungsdemonstration zeigt die Verwendung:
https://ideone.com/8Ft2xu
quelle
operator+
stattoperator*
?(str_hello + str_world)
CSTRING
Makro hinzufügen . Andernfalls können SieCSTRING
innerhalb eines Aufrufs einen[]
Operator nicht erstellen , da double[[
für Attribute reserviert ist.Bearbeiten: Wie Howard Hinnant (und ich in meinem Kommentar zum OP) betonten, benötigen Sie möglicherweise nicht einen Typ mit jedem einzelnen Zeichen der Zeichenfolge als einzelnes Vorlagenargument. Wenn Sie dies benötigen, finden Sie unten eine makrofreie Lösung.
Es gibt einen Trick, den ich beim Kompilieren mit Strings gefunden habe. Neben der "Vorlagenzeichenfolge" muss ein anderer Typ eingeführt werden. Innerhalb von Funktionen können Sie jedoch den Umfang dieses Typs einschränken.
Es werden keine Makros verwendet, sondern einige C ++ 11-Funktionen.
quelle
pair<int,pair<char,double>>
. Ich war stolz auf mich und entdeckte dann diese Antwort und die Metaparse- Bibliothek heute! Ich sollte wirklich SO gründlicher suchen, bevor ich dumme Projekte wie dieses starte :-) Ich denke, dass theoretisch ein vollständig C ++ - Compiler aus dieser Art von Technologie erstellt werden könnte. Was ist das Verrückteste, das damit gebaut wurde?char[]
.my_str.print();
stattstr.print();
?Wenn Sie die Boost-Lösung nicht verwenden möchten, können Sie einfache Makros erstellen, die ähnliche Aktionen ausführen:
Das einzige Problem ist die feste Größe von 64 Zeichen (plus zusätzliche Null). Es kann jedoch leicht je nach Ihren Anforderungen geändert werden.
quelle
sizeof(str) > i
(anstatt die zusätzlichen0,
Token anzuhängen)? Es ist einfach, einetrim
Metafunktion zu definieren , die dies tut, nachdem das Makro bereits aufgerufen wurde, aber es wäre schön, wenn das Makro selbst geändert werden könnte.sizeof(str)
. Es ist möglich, die Zeichenfolgengröße manuell hinzuzufügenMACRO_GET_STR(6, "Hello")
, dies erfordert jedoch Boost-Makros, da das manuelle Schreiben 100-mal mehr Code erfordert (Sie müssen einfache Dinge wie implementieren1+1
).Es gibt einen Artikel: Verwenden von Zeichenfolgen in C ++ - Vorlagenmetaprogrammen von Abel Sinkovics und Dave Abrahams.
Es hat einige Verbesserungen gegenüber Ihrer Idee, Makro + BOOST_PP_REPEAT zu verwenden - es ist nicht erforderlich, eine explizite Größe an das Makro zu übergeben. Kurz gesagt, es basiert auf einer festen Obergrenze für die Zeichenfolgengröße und dem "Schutz vor Zeichenfolgenüberlauf":
plus bedingter Boost :: mpl :: push_back .
Wenn Sie nachfolgende Nullen annehmen, handgeschriebenen Makro Looping, 2x repetion Schnur in der erweiterten Makro, und nicht - Boost haben - dann stimme ich - es ist besser. Bei Boost wären es jedoch nur drei Zeilen:
LIVE DEMO
quelle
Niemand scheint meine andere Antwort zu mögen: - <. Hier zeige ich also, wie man eine str_const in einen realen Typ konvertiert:
Kompiliert mit clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)
quelle
Hier ist eine kurze C ++ 14-Lösung zum Erstellen eines std :: tuple <char ...> für jede übergebene Zeichenfolge zur Kompilierungszeit.
Und hier ist eine zum Erstellen eines eindeutigen Typs zur Kompilierungszeit, der vom anderen Makropost abgeschnitten wurde.
Es ist wirklich schade, dass benutzerdefinierte Literale dafür noch nicht verwendet werden können.
quelle
Ein Kollege forderte mich auf, Zeichenfolgen im Speicher zur Kompilierungszeit zu verketten. Es umfasst auch das Instanziieren einzelner Zeichenfolgen zur Kompilierungszeit. Die vollständige Codeliste finden Sie hier:
quelle
objdump -t a.out |grep my
findet nichts. Als ich anfing, diesen Code einzugeben, experimentierte ich weiter mit dem Entfernenconstexpr
aus den Funktionen undobjdump
zeigte ihnen, wann sieconstexpr
weggelassen wurden. Ich bin zu 99,9% davon überzeugt, dass dies zur Kompilierungszeit geschieht.-S
) ansehen , werden Sie feststellen, dass gcc (4.7.2) dieconstexpr
Funktionen zur Kompilierungszeit tatsächlich auflöst . Die Zeichenfolgen werden jedoch zur Kompilierungszeit nicht zusammengestellt. Vielmehr gibt es (wenn ich es richtig interpretiere) für jedes Zeichen dieser "zusammengesetzten" Zeichenfolgen eine eigenemovb
Operation, die wohl die Optimierung ist, nach der Sie gesucht haben.Basierend auf der Idee von Howard Hinnant können Sie eine Literalklasse erstellen, die zwei Literale addiert.
quelle
str_at
kommt dasstr_at<int I>(const char* a) { return a[i]; }
Ihr Ansatz Nr. 1 ist der richtige.
Nein, nicht richtig. Dies wird mit clang und gcc kompiliert. Ich hoffe, es ist Standard C ++ 11, aber ich bin kein Sprachschreiber.
Was ich für c ++ 17 wirklich lieben würde, wäre das Folgende, um gleichwertig zu sein (um Ansatz 1 zu vervollständigen)
Etwas sehr Ähnliches existiert bereits im Standard für benutzerdefinierte Vorlagenliterale, wie der Void-Zeiger ebenfalls erwähnt, jedoch nur für Ziffern. Bis dahin besteht ein weiterer kleiner Trick darin, den Überschreibungsbearbeitungsmodus + Kopieren und Einfügen von zu verwenden
Wenn Ihnen das Makro nichts ausmacht, funktioniert dies (geringfügig geändert von Yankes Antwort):
quelle
Die Lösung von kacey zum Erstellen eines eindeutigen Typs zur Kompilierungszeit kann mit geringfügigen Änderungen auch mit C ++ 11 verwendet werden:
Verwenden:
quelle
Beim Spielen mit der Boost-Hana-Karte bin ich auf diesen Thread gestoßen. Da keine der Antworten mein Problem löste, fand ich eine andere Lösung, die ich hier hinzufügen möchte, da sie möglicherweise für andere hilfreich sein könnte.
Mein Problem war, dass der Compiler bei Verwendung der Boost-Hana-Map mit Hana-Strings immer noch Laufzeitcode generierte (siehe unten). Der Grund war offensichtlich, dass es sein muss, um die Karte zur Kompilierungszeit abzufragen
constexpr
. Dies ist nicht möglich, da dasBOOST_HANA_STRING
Makro ein Lambda generiert, das nicht imconstexpr
Kontext verwendet werden kann. Andererseits benötigt die Karte Zeichenfolgen mit unterschiedlichem Inhalt, um unterschiedliche Typen zu sein.Da die Lösungen in diesem Thread entweder ein Lambda verwenden oder keine unterschiedlichen Typen für unterschiedliche Inhalte bereitstellen, fand ich den folgenden Ansatz hilfreich. Außerdem wird die hackige
str<'a', 'b', 'c'>
Syntax vermieden .Die Grundidee ist, eine Version von Scott Schurrs
str_const
Vorlage auf dem Hash der Charaktere zu haben. Dies ist möglichc++14
,c++11
sollte aber mit einer rekursiven Implementierung dercrc32
Funktion möglich sein (siehe hier ).Verwendung:
Der resultierende Assembler-Code mit
clang-cl
5.0 lautet:quelle
Ich möchte der Antwort von @ user1115339 zwei sehr kleine Verbesserungen hinzufügen. Ich habe sie in den Kommentaren zur Antwort erwähnt, aber der Einfachheit halber werde ich hier eine Lösung zum Kopieren und Einfügen einfügen.
Der einzige Unterschied ist das
FIXED_CSTRING
Makro, mit dem die Zeichenfolgen in Klassenvorlagen und als Argumente für den Indexoperator verwendet werden können (nützlich, wenn Sie z. B. eine Compiletime-Map haben).Live-Beispiel .
quelle
Meine eigene Implementierung basiert auf dem Ansatz der
Boost.Hana
Zeichenfolge (Vorlagenklasse mit variadischen Zeichen), verwendet jedoch nur denC++11
Standard und dieconstexpr
Funktionen mit strikter Überprüfung der Kompilierbarkeit (wäre ein Kompilierungszeitfehler, wenn kein Kompilierungszeitausdruck). Kann aus der üblichen rohen C-Zeichenfolge anstelle von Phantasie{'a', 'b', 'c' }
(über ein Makro) erstellt werden.Implementierung: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_string.hpp
Tests: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp
Anwendungsbeispiele:
Die Details zu einer
constexpr
Funktion kompilieren den Zeitrahmen: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexprWeitere Verwendungsdetails finden Sie in den Tests.
Das gesamte Projekt ist derzeit experimentell.
quelle
In C ++ 17 mit einer Hilfsmakrofunktion ist es einfach, Zeichenfolgen für die Kompilierungszeit zu erstellen:
Und dies ist ein Anwendungsbeispiel:
quelle