Greboca  

Suport technique et veille technologique

Suport technique et veille technologique

Aujourd’hui, les grandes entreprises et administrations publiques hésitent entre continuer à utiliser des logiciels propriétaires ou basculer vers les Logiciels Libres. Pourtant, la plupart des logiciels libres sont capables de bien traiter les données issues des logiciels propriétaire, et parfois avec une meilleur compatibilité.

C’est alors la barrière de la prise en main qui fait peur, et pourtant...

Les logiciels libres

L’aspect « Logiciel Libre » permet une évolution rapide et une plus grande participation des utilisateurs. Les aides et tutoriels foisonnent sur Internet ou sont directement inclus dans le logiciel lui-même.

Enfin, les concepteurs sont plus proches des utilisateurs, ce qui rend les logiciels libres plus agréable à utiliser et conviviaux.

Grâce à la disponibilité des logiciels libres, vous trouverez facilement des services de support techniques et la licence n’est plus un frein à l’utilisation de ces logiciels par votre personnel.

Notre support technique concerne essentiellement les logiciels libres, que ce soit sous forme de services ponctuels ou de tutoriels.

LinuxFr.org : les journaux  -  C23, listes variantes et le turfu

 -  31 mai - 

Sommaire

Hello again 'nal,

Tu vas sans doute penser que je fais du comique de répétition,

mais là avec toi, je tiens quelque chose : j'ai directement embrayé sur la version suivante de:

variant_list

qui implémente le maximum des plus récentes évolutions du langage C (alias C23).

Pour le coup ça commence vraiment à devenir intéressant.
Ce dont je suis le plus fier est que le code compile désormais sans aucun warning avec la version "15.x staging" de GCC (installable ici pour Debian/Ubuntu, ou ici pour RHEL/Fedora). Cela dit, normalement tout est déjà dans GCC 14. Qu'Arch Linux a sûrement déjà 😉.

Le composant lui-même, une liste hétérogène, peut se révéler très utile ; le but premier reste cependant de "démontrer C23", aux détriments de certaines optimisations.

Je vais les lister ci-après (pas toutes, et avec 2-3 que je n'ai pas -encore ?- utilisées mais qui valent le coup d'oeil).


1) auto

L'inférence de type made in C++ fait son entrée sous une forme affaiblie.

auto v = (Value*) calloc(1, sizeof(Value));  // le compilateur déduit "Value*"

ou

auto l = list_create(0); // le compilateur déduit "List*" par la signature

On peut bien l'utiliser avec des pointeurs, comme ici "Value*" et "List*", contrairement à ce que laisse supposer la spécification ; c'est uniquement la syntaxe "auto* var" qui est interdite.

J'ai dit "affaibli" car cela reste une déduction faite à la compilation, pas à l'exécution. Ça veut dire que ce genre de raffinement p.ex.:

auto var = (list_get_Type(l, idx) == EINTEGER) ? list_grab_int(l, idx) : ... // tester les autres types

qui aurait permis un type de retour variable et donc un "var" de type dynamique, reste impossible… pour l'instant !

2) typeof()

Lié au précédent, et à la compilation également ;
pour éviter les répétions ou d'exposer les détails d'implémentation, on peut faire :

for (typeof(val->idx) i = 0;  i < val->idx - 1;  i++) {  // size_t idx

Tant qu'on sait qu'il est de la famille des entiers, on n'a pas besoin de connaître le type précis de "i" (ici "size_t") pour le définir comme itérateur de notre boucle.

Dans le cas des APIs publiques, j'ai ajouté une petit macro sympa -quoi qu'encore sous-exploitée- qui en démontre un usage possible :

for (TYPEOF(list_length) i = 0;  i < list_length(l);  i++)

et qui récupère le type de retour des fonctions supportant la syntaxe "fonction(nullptr, …) -ce qui nécessite bien sûr d'être pensé en amont !
Et le code de ladite macro, justement, utilise…

3) --VA--OPT--(sym)

Utilisée par exemple comme:

define TYPEOF(F, ...) typeof(F(nullptr __VA_OPT__(,) __VA_ARGS__))

cette nouvelle macro plus générique remplace l'ancienne macro GCC non-standard "##VA_ARGS" ;
elle omet dans notre cas la virgule "," si l'appel n'a qu'un argument (p.ex. "TYPEOF(fn)" -> "fn(nullptr)").

4) nullptr

NULL est historiquement défini comme "void()()" par GCC, mais "(int)0" par G++ et… d'autres compilateurs.
Cela peut avoir des conséquences subtiles sur le code, notamment sur les macros _Generic ajoutées en C11 et donc je fais un usage intensif.
"nullptr" conserve les propriétés de cast universel de NULL, mais normalise sa définition sur celle de C++, distinct à la fois de "void*" et "int".

char* err = nullptr;
if (!err) err = "test";

Avec les macros _Generic justement, il ne faut pas oublier de gérer son type propre "nullptr_t" :

#define list_set(L, I, V) _Generic((V), \
  int:       list_set_int, \               // 0
  void*:     list_set_void, \              // NULL 
  nullptr_t: list_set_nullptr)(L, I, V)    // nullptr

_

5) fallthrough

Avec tous les warnings activés, GCC en émet un quand un "switch() { case: .." n'est pas délimité par un "break;" (ce qui est en général une erreur).
Cette nouvelle annotation permet d'indiquer que c'est bien l'intention du développeur d'enchaîner :

switch (res) {
    case EINVAL:  if (!err) err="Invalid index";  [[fallthrough]];
    case EAGAIN:  if (!err) err="List locked";    [[fallthrough]];
    case EUNDEF:  if (!err) err="Undefined value";
        fprintf(stderr, "[ERR: %s]\n", err);

_

6) nodiscard

Imaginez une fonction "list_empty()" supposée être utilisée ainsi

if (list_empty(l)) {

sauf que le stagiaire a compris tout à fait autre chose :

list_empty(l); // clear list for future re-use

Un warning peut être explicitement ajouté sur ces maladresses :

[[nodiscard("Did not test the return value!")]]
bool list_empty(List* list);

qui sont parfois bien pires : pensez à "list_create()" qui renvoie un pointeur alloué sur le tas, qu'on est censé libérer…

7) "bool" intégré

C'est un point mineur, mais "bool" n'est désormais plus un "#define 1-#define 0",
c'est un type intégré du langage, distinct de "int", et ne nécessitant plus d'inclure "stdbool.h".

L'avantage est entre autres une bonne gestion par les macros _Generic :

errno_t list_add_int(List* list, int i);
errno_t list_add_bool(List* list, bool b);

#define list_add(L, V) _Generic((V), \
    int:    list_add_int, \
    bool:   list_add_bool, \

_

8) Paramètres optionnels

Un paramètre de fonction peut être non-nommé dans une implémentation,
ce qui ne produit plus de warning genre "Paramètre inutilisé":

errno_t _value_get_Type(Value* v, void*) // "void*" pas utilisé
{
    switch (v->t) {
      case T_INTEGER: return EINTEGER;
      case T_BOOLEAN: return EBOOLEAN;
      case T_FLOAT  : return EFLOAT;
      case T_STRING : return ESTRING;
      default       : return EUNDEF;
    }
}

Il y a au moins deux usages intéressants :
- les pointeurs de fonction ; pour p.ex. leur donner une signature identique, mais n'utiliser qu'une partie des paramètres selon le cas ;
-les sélections par macros _Generic comme ici.

9) {} (initialiseur vide)

Ce code propre à GCC et Clang :

 // does not compile with MSVC
struct MyStruct s = {0}; // all bits are 0 except sometimes with "-02,-03"

qui initalise tous les champs à 0 (y compris le "padding" rajouté selon l'architecture),
mais ne fonctionnait pas avec tous les compilateurs ni tous les niveaux d'optimisation (obligeant à recourir p.ex. à "memset()"),
est désormais normalisé sous la forme :

struct MyStruct s = {};  // all bits are 0, on all compilers & levels

_

11) constexpr

On peut désormais définir des expressions constantes déterminées à la compilation.
Alors ça permet des subtilités auxquelles on ne pense pas forcément :

const int a = 5 + 1;
int arr_a[a];      // tableau de taille variable
memset(arr_a, 0, sizeof(arr_a));

constexpr int b = 5 + 1;
int arr_b[b] = {}; // tableau de taille statique à 6

_

12) ckd_*() (dépassement de bornes)

Historiquement, le standard ne définit pas ce qu'il se passe lorsqu'on fait dépasser ses bornes à un nombre :

int i = 2147483647 + 1; // "-214748364" on GCC without "-fwrapv"

cela se contrôlait, mal, avec des flags propres aux compilateurs comme "-fwrapv".
En plus d'être sources d'erreur complexes, cela ne faisait pas très sérieux pour un langage se vantant de sa performance en calculs mathématiques…
On peut désormais contrôler programmatiquement le comportement des dépassements de bornes :

#include 

uint32_t res;

if (ckd_add(&res, 2147483647, 1)) {
  fprintf(stderr, "OVERFLOW! Limiting to upper limit...\n");
  res = INT_MAX;   // 2147483647
}

Il est facile, partant de là, de créer des fonctions du genre "check_add()" effectuant des calculs sûrs.


Voilà !
Comme la dernière fois, j'ai également fait une version bibliothèque du composant sous LGPLv3.
Et je n'ai toujours pas mis de Makefile générique 😉.

Commentaires : voir le flux Atom ouvrir dans le navigateur

par Tarnyko

LinuxFr.org : les journaux

LinuxFr.org : Journaux

Nouvelle version de Grammalecte pour Firefox

 -  30 août - 

Grammalecte est un correcteur grammatical et typographique libre, pour la langue française. Il est bien connu sur LinuxFr.org (voir l’étiquette) : (...)


Taack-ui version 0.5.4

 -  23 août - 

Bonjour à toutes et tous,TaackUI est un framework (ou cadriciel) pour créer des applications d’entreprise (c.a.d. beaucoup de formulaires, aspect (...)


Fin d’OCSP chez Let’s Encrypt : quid ?

 -  15 août - 

Sommaire Contexte Certificat X.509 et autorité de certification Let’s Encrypt Validation du certificat OCSP - Online Certificate Status Protocol, (...)


Proxmox_GK : un utilitaire shell pour déployer vos invités LXC/QEMU, avec Cloud-init

 -  3 août - 

Sommaire Que peut-il faire ? Modes de déploiement flexible Customisation et templating Structure fonctionnelle Conclusion Aller plus loin : (...)


AES-XTS dans le noyau Linux 6.10

 -  23 juillet - 

Le dernier noyau Linux publié est le 6.10 et il incorpore le travail d'Eric Biggers qui a cherché à optimiser les performances de l'algorithme de (...)