Être capable de créer et de manipuler des chaînes pendant la compilation en C ++ a plusieurs applications utiles. Bien qu'il soit possible de créer des chaînes de compilation en C ++, le processus est très lourd, car la chaîne doit être déclarée comme une séquence variadique de caractères, par exemple
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Des opérations telles que la concaténation de chaînes, l'extraction de sous-chaînes et bien d'autres peuvent facilement être implémentées en tant qu'opérations sur des séquences de caractères. Est-il possible de déclarer des chaînes de compilation plus facilement? Sinon, y a-t-il une proposition dans les travaux qui permettrait une déclaration pratique des chaînes de compilation?
Pourquoi les approches existantes échouent
Idéalement, nous aimerions pouvoir déclarer des chaînes de compilation comme suit:
// Approach 1
using str1 = sequence<"Hello, world!">;
ou, en utilisant des littéraux définis par l'utilisateur,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
où decltype(str2)
aurait un constexpr
constructeur. Une version plus désordonnée de l'approche 1 est possible à mettre en œuvre, en tirant parti du fait que vous pouvez effectuer les opérations suivantes:
template <unsigned Size, const char Array[Size]>
struct foo;
Cependant, le tableau devrait avoir un lien externe, donc pour que l'approche 1 fonctionne, nous devions écrire quelque chose comme ceci:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
Inutile de dire que c'est très gênant. L'approche 2 n'est en fait pas possible à mettre en œuvre. Si nous devions déclarer un constexpr
opérateur littéral ( ), comment spécifierions-nous le type de retour? Puisque nous avons besoin de l'opérateur pour renvoyer une séquence variadique de caractères, nous aurions donc besoin d'utiliser le const char*
paramètre pour spécifier le type de retour:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Cela entraîne une erreur de compilation, car ce s
n'est pas un fichier constexpr
. Essayer de contourner ce problème en procédant comme suit n'aide pas beaucoup.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
La norme stipule que cette forme d'opérateur littéral spécifique est réservée aux types entiers et flottants. Cela 123_s
fonctionnerait, mais abc_s
pas. Et si nous abandonnions complètement les littéraux définis par l'utilisateur et utilisions simplement une constexpr
fonction régulière ?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Comme auparavant, nous nous heurtons au problème que le tableau, maintenant un paramètre de la constexpr
fonction, n'est plus un constexpr
type.
Je pense qu'il devrait être possible de définir une macro de préprocesseur C qui prend une chaîne et la taille de la chaîne comme arguments, et renvoie une séquence composée des caractères de la chaîne (utilisation BOOST_PP_FOR
, stringification, indices de tableau, etc.). Cependant, je n'ai pas le temps (ou assez d'intérêt) pour implémenter une telle macro =)
la source
constexpr
fonctions et initialiser des tableaux (par conséquent, concat, substr, etc.).constexpr
chaînes peuvent être analysées pendant la compilation, de sorte que vous pouvez prendre différents chemins de code en fonction des résultats. Essentiellement, vous pouvez créer des EDL dans C ++; les applications sont assez illimitées.Réponses:
Je n'ai rien vu qui corresponde à l'élégance de Scott Schurr's
str_const
présenté à C ++ Now 2012 . Cela nécessiteconstexpr
cependant.Voici comment vous pouvez l'utiliser et ce qu'il peut faire:
Cela ne devient pas beaucoup plus cool que la vérification de la plage de compilation!
Tant l'utilisation que l'implémentation sont exemptes de macros. Et il n'y a pas de limite artificielle sur la taille des cordes. Je publierais l'implémentation ici, mais je respecte le droit d'auteur implicite de Scott. La mise en œuvre est sur une seule diapositive de sa présentation liée à ci-dessus.
la source
str_const
et l'autre basée sursequence
), cela peut être possible. L'utilisateur l'utiliserastr_const
pour initialiser la chaîne, mais les opérations suivantes qui créent de nouvelles chaînes renverront dessequence
objets.template<char... cs>
. En théorie, vous pouvez créer quelque chose qui prend une chaîne littérale et compile le contenu en une fonction. Voir la réponse par dyp. Une bibliothèque très complète est métaparse . Essentiellement, vous pouvez définir n'importe quel mappage des chaînes littérales aux types et l'implémenter avec ce type de technologie.constexpr operator==
. Désolé. La présentation de Scott devrait vous aider à démarrer. C'est beaucoup plus facile en C ++ 14 qu'en C ++ 11. Je ne prendrais même pas la peine d'essayer en C ++ 11. Voir les dernièresconstexpr
discussions de Scott ici: youtube.com/user/CppConil est possible de l'implémenter sans compter sur boost, en utilisant une macro très simple et certaines fonctionnalités de C ++ 11:
(les deux derniers ne sont pas strictement requis ici)
nous devons être en mesure d'instancier un modèle variadique avec des indices fournis par l'utilisateur de 0 à N - un outil également utile par exemple pour développer un tuple dans l'argument de la fonction de modèle variadique (voir les questions: Comment puis-je développer un tuple dans les arguments de la fonction de modèle variadique?
" décompresser "un tuple pour appeler un pointeur de fonction correspondant )
puis définissez un modèle variadique appelé string avec un paramètre non de type char:
maintenant la partie la plus intéressante - pour passer des littéraux de caractères dans un modèle de chaîne:
une simple démonstration de concaténation montre l'utilisation:
https://ideone.com/8Ft2xu
la source
operator+
au lieu deoperator*
?(str_hello + str_world)
CSTRING
macro. Sinon, vous ne pouvez pas créer unCSTRING
appel interne à un[]
opérateur, car les doubles[[
sont réservés aux attributs.Edit: comme Howard Hinnant (et moi-même dans mon commentaire à l'OP) l'a souligné, vous n'aurez peut-être pas besoin d'un type avec chaque caractère de la chaîne comme argument de modèle unique. Si vous en avez besoin, il existe une solution sans macro ci-dessous.
J'ai trouvé une astuce en essayant de travailler avec des chaînes au moment de la compilation. Il nécessite d'introduire un autre type en plus de la "chaîne de modèle", mais dans les fonctions, vous pouvez limiter la portée de ce type.
Il n'utilise pas de macros mais plutôt certaines fonctionnalités C ++ 11.
la source
pair<int,pair<char,double>>
. J'étais fier de moi et j'ai découvert cette réponse, ainsi que la bibliothèque métaparse aujourd'hui! Je devrais vraiment chercher SO plus en profondeur avant de démarrer des projets stupides comme celui-ci :-) Je suppose qu'en théorie, un compilateur entièrement C ++ pourrait être construit à partir de ce type de technologie. Quelle est la chose la plus folle qui ait été construite avec ça?char[]
.my_str.print();
au lieu destr.print();
?Si vous ne souhaitez pas utiliser la solution Boost, vous pouvez créer des macros simples qui feront quelque chose de similaire:
Le seul problème est la taille fixe de 64 caractères (plus zéro supplémentaire). Mais il peut facilement être modifié en fonction de vos besoins.
la source
sizeof(str) > i
(au lieu d'ajouter les0,
jetons supplémentaires )? Il est facile de définir unetrim
métafonction qui le fera après que la macro a déjà été appelée, mais ce serait bien si la macro elle-même pouvait être modifiée.sizeof(str)
. Il est possible d'ajouter manuellement une taille de chaîne commeMACRO_GET_STR(6, "Hello")
mais cela nécessite des macros Boost pour fonctionner car l'écriture manuelle nécessite 100 fois plus de code (vous avez besoin d'implémentations simples comme1+1
).Il y a un article: Utilisation de chaînes dans les métaprogrammes de modèle C ++ par Abel Sinkovics et Dave Abrahams.
Il a une certaine amélioration par rapport à votre idée d'utiliser la macro + BOOST_PP_REPEAT - il ne nécessite pas de passer une taille explicite à la macro. En bref, il est basé sur une limite supérieure fixe pour la taille de la chaîne et la "protection contre le dépassement de chaîne":
plus boost conditionnel :: mpl :: push_back .
Si vous acceptez les zéros de fin, les boucles de macro manuscrites, la répétition 2x de la chaîne dans la macro développée et que vous n'avez pas Boost - alors je suis d'accord - c'est mieux. Cependant, avec Boost, ce ne serait que trois lignes:
DÉMO EN DIRECT
la source
Personne ne semble aimer mon autre réponse: - <. Donc, ici, je montre comment convertir un str_const en un type réel:
Compile avec clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)
la source
Un collègue m'a mis au défi de concaténer des chaînes en mémoire au moment de la compilation. Il inclut également l'instanciation de chaînes individuelles au moment de la compilation. La liste complète des codes est ici:
la source
objdump -t a.out |grep my
ne trouve rien. Quand j'ai commencé à taper ce code, j'ai continué à expérimenter la suppressionconstexpr
des fonctions etobjdump
leur ai montré quandconstexpr
était omis. Je suis sûr à 99,9% que cela se produit au moment de la compilation.-S
), vous remarquerez que gcc (4.7.2) résout effectivement lesconstexpr
fonctions au moment de la compilation. Pourtant, les chaînes ne sont pas assemblées au moment de la compilation. Au contraire, (si je l'interprète correctement) pour chaque caractère de ces chaînes "assemblées", il y a une propremovb
opération, qui est sans doute l'optimisation que vous recherchiez.Voici une solution succincte en C ++ 14 pour créer un std :: tuple <char ...> pour chaque chaîne passée au moment de la compilation.
Et en voici un pour créer un type de compilation unique, réduit à partir de l'autre publication de macro.
C'est vraiment dommage que les littéraux définis par l'utilisateur ne puissent pas encore être utilisés pour cela.
la source
basé sur l'idée de Howard Hinnant, vous pouvez créer une classe littérale qui ajoutera deux littéraux ensemble.
la source
str_at
vient-il?str_at<int I>(const char* a) { return a[i]; }
Votre approche n ° 1 est la bonne.
Non, pas correct. Cela compile avec clang et gcc. J'espère que sa norme c ++ 11, mais je ne suis pas un prof de langage.
Ce que j'aimerais vraiment pour c ++ 17 serait le suivant pour être équivalent (pour compléter l'approche n ° 1)
Quelque chose de très similaire existe déjà dans la norme pour les littéraux définis par l'utilisateur, comme le mentionne également void-pointer, mais uniquement pour les chiffres. Jusque-là, une autre petite astuce consiste à utiliser le mode d'édition de remplacement + copier et coller de
Si la macro ne vous dérange pas, cela fonctionne (légèrement modifié par rapport à la réponse des Yankes):
la source
La solution de kacey pour créer un type de compilation unique peut, avec des modifications mineures, également être utilisée avec C ++ 11:
Utilisation:
la source
En jouant avec la carte boost hana, je suis tombé sur ce fil. Comme aucune des réponses n'a résolu mon problème, j'ai trouvé une solution différente que je veux ajouter ici car elle pourrait être potentiellement utile pour d'autres.
Mon problème était que lors de l'utilisation de la carte boost hana avec des chaînes hana, le compilateur générait toujours du code d'exécution (voir ci-dessous). La raison était évidemment que pour interroger la carte au moment de la compilation, cela devait l'être
constexpr
. Ce n'est pas possible car laBOOST_HANA_STRING
macro génère un lambda, qui ne peut pas être utilisé enconstexpr
contexte. D'autre part, la carte a besoin de chaînes avec un contenu différent pour être de types différents.Comme les solutions de ce fil utilisent un lambda ou ne fournissent pas différents types pour différents contenus, j'ai trouvé l'approche suivante utile. Cela évite également la
str<'a', 'b', 'c'>
syntaxe hacky .L'idée de base est d'avoir une version du
str_const
modèle de Scott Schurr sur le hachage des personnages. C'estc++14
, maisc++11
devrait être possible avec une implémentation récursive de lacrc32
fonction (voir ici ).Usage:
Le code assembleur résultant avec
clang-cl
5.0 est:la source
J'aimerais ajouter deux très petites améliorations à la réponse de @ user1115339. Je les ai mentionnés dans les commentaires de la réponse, mais pour plus de commodité, je vais mettre une solution de copier-coller ici.
La seule différence est la
FIXED_CSTRING
macro, qui permet d'utiliser les chaînes dans les modèles de classe et comme arguments de l'opérateur d'index (utile si vous avez par exemple une carte de compilation).Exemple en direct .
la source
Ma propre implémentation est basée sur l'approche de la
Boost.Hana
chaîne (classe de modèle avec des caractères variadiques), mais n'utilise que leC++11
standard et desconstexpr
fonctions avec un contrôle strict de la compatibilité (ce serait une erreur de compilation si ce n'est une expression de temps de compilation). Peut être construit à partir de la chaîne C brute habituelle au lieu de fantaisie{'a', 'b', 'c' }
(via une macro).Mise en œuvre: 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
Exemples d'utilisation:
Les détails sur une
constexpr
limite de temps de compilation de fonction: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexprPour d'autres détails d'utilisation, consultez les tests.
L'ensemble du projet est actuellement expérimental.
la source
En C ++ 17 avec une fonction de macro d'assistance, il est facile de créer des chaînes de compilation:
Et voici un exemple d'utilisation:
la source