Quand je vois cela, j'ai envie de serrer chaleureusement la main de l'auteur.
Puis tant que je la tiens, je lui brise les doigts en espérant que ça suffira pour qu'il n'utilise plus jamais un clavier.
Ce n'est pas de la haine, c'est que... comment dire, c'est dur à expliquer... certaines choses devraient rester dans les abysses, ne jamais être révélées aux yeux des hommes. C'est comme voir quelqu'un en train d'invoquer un Grand Ancien : tu SAIS que tu dois l'arrêter avant qu'il ne soit trop tard.
« Nous avons propagé sur Extranet une histoire fabriquée de toutes pièces selon laquelle une certaine disposition d'étoiles, vue depuis la planète d'origine des butariens, formaient le visage d'une déesse galarienne.
Sans chercher à vérifier ces informations, certains ont décrété que c'était la preuve de l'existence de la déesse. Ceux qui notaient le manque de preuves se faisaient attaquer. »
— Legion, geth trolleur à portée galactique
Nil Le 07/09/2018 à 23:23 • Nil commence une incantation
C'est excellent comme exploit! Cela dit, je signale que cette grammaire n'est pas du tout régulière, ni même algébrique (non contextuelle), son "expression régulière" abuse massivement des regards en avant (lookaheads) et des références en arrière (backreferences).
Ca serait plus propre avec les concepts mais c'est TS C++20 donc je peux me gratter pour le moment
Le PLUS CHIANT c'est que les infos de genericite sont "perdues"... jmexplique,
template <typename T>
struct rebinder;
template <template <typename...> typename Base, typename... BaseArgs>
struct rebinder<Base<BaseArgs...>>
{
template <typename... Args>
using rebound = Base<Args...>;
};
(Exemple aligne en 2 secondes)
L'interet de ce truc c'est de pouvoir remapper des types a N parametres generiques en 1 ligne, p-ex
using pair_i2 = std::pair<int, int>;
using pair_f2 = typename rebinder<pair_i2>::template bound<float, float>; // Revient a std::pair<float, float>
L'exemple est evidemment completement con mais c'est la base pour les concepts.
Le seul probleme c'est que si maintenant j'ai une methode generique qui prend une pair<T1, T2>:
template <typename Pair>
void faire_choses_coquines_avec_paire(Paire const& paire) {
// Telephone rose du C++
}
Admettons que maintenant tu ne saches pas que c'est une paire, mais tu veux rebind vers pair<float, float> (Le compilo te casse la gueule si tu essaie de faire pair<float, float, float> etc)
template <typename Paire>
void faire_choses_pas_trop_jolies(Paire paire)
{
using paire_floats_t = typename rebinder<Paire>::template bound<float, float>;
paire_floats_t paire_floats_instance;
}
Ben ca compile pas. Pourquoi? Parce que le compilo voit qu'on veut un rebinder<T>, soit la classe de base, et va donc gentillement te dire que cette derniere n'a pas de membre nomme bound.
Du coup la solution? Ceci:
template <template <typename, typename> typename Paire, typename A, typename B>
void faire_choses_sales(Paire<A, B> const& paire)
{
using paire_floats = typename rebinder<Paire<A, B>>::template bound<float, float>;
paire_floats ffs;
}
Et la c'est tout bon, parce qu'on choisit la specialisation partielle qui expose bound.
Je pense pas que ce soit un bug pour autant, mais ca a du etre choisi comme ca pour pas rendre les compilo C++ encore plus usine-a-gaz-esque, et ca reste terriblement chiant. C'est pour ca que je me retrouve avec 4 specialisations partielles pour ma classe container_traits, parce que chaque container a un nombre differents de parametres generiques (pour le moment!), et que c'est pas evident d'aller chercher le N-ieme element d'un parameter pack (typename... Args), surtout quand N change d'un type a l'autre.
Par exemple la je suis coince car unordered_map expose un type dependant nomme value_type, qui vaut .... pair<const Clef, Valeur>. Mais a mes yeux, c'est un truc opaque, donc pour revenir a mon exemple, ben je peux pas reutiliser le type pair qu'un utilisateur foutrait dans sa librarie a facon STL, donc j'suis oblige d'imposer std::pair, et l'user se retrouve a jongler apres (operateurs de cast implicites / constructeurs par deplacement etc)
Bref c'est le bordel mais ca s'arrangera (faut esperer) avec les concepts, ou la on parlera plus telllement de templates mais plutot de features d'un type (constructible par copie, constructible par defaut, iterable dans un sens, dans les deux, etc)
Donc en clair, c'est de la magie noire pour convertir foo<T1> en foo<T2>, voire avec plusieurs types paramètre comme dans ton exemple avec les paires? Je comprends mieux comment fonctionne ton snippet maintenant. Le but du snippet que tu as linké est de faire une opération map comme dans un langage fonctionnel, c'est bien ça? Donc au moins je comprends ta fonction main. L'implémentation, elle (donc tout ce qui est dans ton namespace xstd), reste de la magie noire.
GCC compile ça aussi, en tout cas. (J'ai essayé dans Godbolt.)
C'est quand même violemment inbranlable la syntaxe du C++ ##gerbe##
SCPCD : ça marche aussi si tu remplaces "C++" par "Java" (ou "Javascript", ou "PHP"...)
—
Zeroblog —
« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » —
Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » —
GT Turbo