La GSL est une petite bibliothèque de facilités conçue pour soutenir cet ensemble de lignes directrices.
Sans ces facilités, les lignes directrices devraient être bien plus restrictives sur les détails du langage.
La bibliothèque de support des lignes directrices se définit dans l’espace de noms gsl et les noms peuvent être des alias pour les noms de la bibliothèque standard ou d’autres bibliothèques bien connues. L’utilisation de l’indirection (temps de compilation) via l’espace de noms gsl permet l’expérimentation et des variantes locales des facilités de support.
La GSL est uniquement une bibliothèque d’en‑tête, et se trouve à l’adresse
GSL : Bibliothèque de support des lignes directrices.
Les facilités de la bibliothèque de support sont conçues pour être extrêmement légères (zéro‑surcoût) afin de ne pas imposer d’encombrement par rapport à l’utilisation d’alternatives conventionnelles.
Lorsqu’il est souhaité, elles peuvent être « instrumentées » avec des fonctionnalités supplémentaires (par ex., des vérifications) pour des tâches telles que le débogage.
Ces lignes directrices utilisent des types provenant de la bibliothèque standard (p. ex., C++17) en plus de ceux de la GSL.
Par ex., nous supposons un type variant, mais celui‑ci n’est pas encore présent dans la GSL.
À l’avenir, utilisez le type adopté dans C++17.
Certains types GSL listés ci‑dessous pourraient ne pas être pris en charge dans la bibliothèque que vous utilisez en raison de contraintes techniques telles que les limites des versions actuelles de C++.
Veuillez consulter la documentation de votre GSL pour en savoir plus.
Pour chaque type GSL ci‑dessous, nous énonçons un invariant pour ce type.
Cet invariant reste valable tant que le code utilisateur ne modifie que l’état d’un objet GSL en utilisant les fonctions membres ou libres fournies par ce type (c.-à-d. le code utilisateur ne contournent pas l’interface du type pour modifier la valeur/les bits de l’objet en violant une autre règle des lignes directrices).
Résumé des composants GSL :
Nous prévoyons une spécification semi‑formelle « ISO C++ standard style » de la GSL.
Nous nous appuyons sur la bibliothèque standard ISO C++ et espérons que certaines parties de la GSL seront intégrées dans la bibliothèque standard.
Ces types permettent à l’utilisateur de distinguer les pointeurs propriétaires et non‑propriétaires ainsi que les pointeurs vers un seul objet et ceux vers le premier élément d’une séquence.
Ces « vues » ne sont jamais propriétaires.
Les références ne sont jamais propriétaires (voir R.4). @TODO-LINK
Note : les références ont de nombreuses occasions de dépasser la durée de vie des objets auxquels elles se réfèrent (retourner une variable locale par référence, conserver une référence à un élément d’un vecteur et appeler push_back, lier à std::max(x, y + 1), etc.). Le profil de sécurité de durée de vie vise à résoudre ces problèmes, mais même ainsi owner<T&> n’a aucun sens et est découragé.
Les noms sont majoritairement de style ISO de la bibliothèque standard (minuscules et tiret bas) :
T* // Le T* n’est pas un propriétaire, peut être nul ; supposé pointer vers un seul élément.T& // Le T& n’est pas un propriétaire et ne peut jamais être une « null reference » ; les références sont toujours liées à des objets.La notation « pointeur brut » (p. ex. int*) est supposée avoir son sens le plus commun ; c’est‑à‑dire qu’un pointeur pointe vers un objet, mais ne le possède pas.
Les propriétaires doivent être convertis en gestionnaires de ressources (p. ex., unique_ptr ou vector<T>) ou marqués owner<T*>.
owner<T*> // un T* qui possède l’objet pointé/référencé ; peut être nullptr.owner est utilisé pour marquer les pointeurs propriétaires dans le code qui ne peut pas être mis à niveau vers des gestionnaires de ressources appropriés.
Les raisons sont :
Un owner<T> diffère d’un gestionnaire de ressources pour un T par le fait qu’il requiert toujours un delete.
Un owner<T> est supposé référencer un objet dans le tas (heap).
Si quelque chose ne doit pas être nullptr, précisez‑le :
not_null<T> // T est habituellement un type pointeur (p. ex., not_null<int*> et not_null<owner<Foo*>>) qui ne doit pas être nullptr.
T peut être n’importe quel type pour lequel ==nullptr a du sens.
span<T> // [p:p+n), constructeur à partir de {p, q} et {p, n} ; T est le type de pointeurspan_p<T> // {p, predicate} [p:q) où q est le premier élément pour lequel predicate(*p) est vraiUn span<T> fait référence à zéro ou plusieurs T mutables à moins que T ne soit un type const.
Tous les accès aux éléments du span, notamment via operator[], sont garantis d’être vérifiés par les limites par défaut.
Remarque : le
spande la GSL (initialement appeléarray_view) a été proposé pour être inclus dans la bibliothèque standard C++, et a été adopté (avec des modifications de son nom et de son interface) sauf questd::spanne fournit pas de vérification garantie des limites. Par conséquent, la GSL a changé le nom et l’interface despanpour suivrestd::spanet devrait être exactement le même questd::span, et la seule différence devrait être quegsl::spanest totalement sûr aux limites par défaut.
Si la sûreté des limites pouvait affecter son interface, ces propositions de changement devraient être ramenées via le comité ISO C++ pour gardergsl::spancompatible avecstd::span.
Si une évolution future destd::spanajoute une vérification des limites,gsl::spanpeut être supprimé.
« L’arithmétique de pointeur » se fait de préférence dans les span.
Un char* qui pointe vers plus d’un char mais n’est pas une chaîne de caractères de style C (p. ex., un pointeur dans un tampon d’entrée) devrait être représenté par un span.
zstring // un char* supposé être une chaîne de caractères de style C ; c’est‑à‑dire une séquence terminée par zéro de char ou nullptrczstring // un const char* supposé être une chaîne de caractères de style C ; c’est‑à‑dire une séquence terminée par zéro de const char ou nullptrLogiquement, ces deux alias ne sont pas nécessaires, mais nous ne sommes pas toujours logiques, et ils rendent explicite la distinction entre un pointeur vers un seul char et un pointeur vers une chaîne de caractères de style C.
Une séquence de caractères qui n’est pas supposée être terminée par zéro devrait être un span<char>, ou s’il est impossible à cause de problèmes d’ABI un char*, plutôt qu’un zstring.
Utilisez not_null<zstring> pour les chaînes de caractères de style C qui ne peuvent pas être nullptr.
??? Est‑ce qu’on a besoin d’un nom pour not_null<zstring> ? Ou est‑ce son impopularité ?
unique_ptr<T> // propriété unique : std::unique_ptr<T>shared_ptr<T> // propriété partagée : std::shared_ptr<T> (pointeur compté)stack_array<T> // un tableau alloué sur la pile. Le nombre d’éléments est déterminé lors de la construction et reste fixe par la suite. Les éléments sont mutables sauf si T est un type const.dyn_array<T> // un conteneur, tableau alloué dynamiquement sans croissance. Le nombre d’éléments est déterminé lors de la construction et reste fixe par la suite.T est un type const. Essentiellement un span qui alloue et possède ses éléments.Expects // assertion de précondition. Actuellement placée dans les corps de fonction. Plus tard, doit être déplacée aux déclarations.Expects(p) termine le programme à moins que p == trueExpects est sous le contrôle de quelques options (enforcement, message d’erreur, alternatives pour terminer)Ensures // assertion de postcondition. Actuellement placée dans les corps de fonction. Plus tard, devrait être déplacée aux déclarations.Ces assertions sont actuellement des macros (cahottes !) et doivent apparaître uniquement dans les définitions de fonction, en attendant les décisions du comité standard sur les contrats et la syntaxe d’assertion.
Consultez la proposition de contrat ; en utilisant la syntaxe d’attribut, par ex., Expects(p) deviendra [[expects: p]].
finally // finally(f) crée un final_action{f} avec un destructeur qui invoque fnarrow_cast // narrow_cast<T>(x) est static_cast<T>(x)narrow // narrow<T>(x) est static_cast<T>(x) si static_cast<T>(x) == x sans promotions de signes, sinon jette narrowing_error (par ex., narrow<unsigned>(-42) jette)[[implicit]] // « Marker » pour placer sur les constructeurs à un seul argument afin de les rendre explicitement non‑explicites.move_owner // p = move_owner(q) signifie p = q mais ???joining_thread // une version RAII de std::thread qui rejoint.index // un type à utiliser pour l’indexation de tous les conteneurs et tableaux (actuellement alias de ptrdiff_t)Ces concepts (prédicats de type) sont empruntés à la bibliothèque Origin d’Andrew Sutton, la proposition Range, et l’TR de l’ISO WG21 Palo Alto. Beaucoup d’entre eux sont très similaires à ce qui est devenu partie du standard ISO C++ en C++20.
StringNumberBooleanRange // en C++20, std::ranges::rangeSortable // en C++20, std::sortableEqualityComparable // en C++20, std::equality_comparableConvertible // en C++20, std::convertible_toCommon // en C++20, std::common_withIntegral // en C++20, std::integralSignedIntegral // en C++20, std::signed_integralSemiRegular // en C++20, std::semiregularRegular // en C++20, std::regularTotallyOrdered // en C++20, std::totally_orderedFunction // en C++20, std::invocableRegularFunction // en C++20, std::regular_invocablePredicate // en C++20, std::predicateRelation // en C++20, std::relationPointer // Un type avec *, ->, ==, et construction par défaut (la construction par défaut sert à établir la valeur « null »)Unique_pointer // Un type correspondant à Pointer, est déplaçable, et n’est pas copiableShared_pointer // Un type correspondant à Pointer, et est copiable