3 points par GN⁺ 2025-05-31 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • C3 est basé sur le langage C et fournit des fonctionnalités avancées comme les modules, la surcharge d’opérateurs, les génériques et l’exécution à la compilation
  • Tout en conservant une syntaxe C familière, il intègre des constructions qui renforcent la productivité et la robustesse comme la gestion des erreurs, defer et foreach
  • L’introduction de contrats déclaratifs ainsi que des types optionnels et d’un modèle de gestion des erreurs améliore la sécurité et la clarté
  • L’environnement de développement prend en charge une bibliothèque standard et un système de build intégrés, ainsi que l’allocation mémoire temporaire et d’autres aspects pratiques
  • On y retrouve des similitudes avec le langage Zig dans le build, la création de projet et la structure du code, ce qui laisse entrevoir de nouvelles expérimentations de conception de langage

Vue d’ensemble et caractéristiques de C3

Qu’est-ce que C3 ?

  • C3 est un langage construit sur le C existant, qui conserve une syntaxe familière tout en apportant des fonctionnalités difficiles à obtenir en C, comme un système de modules, la surcharge d’opérateurs, les génériques, l’exécution à la compilation, la gestion des erreurs, defer, les value methods, des contracts progressifs, les slices, foreach et le support des types dynamiques
  • Sa structure modulaire, fondée sur des namespaces, évite les conflits de noms (abc::Context illustre un namespace impératif)
  • Son objectif principal est d’augmenter la productivité tout en fournissant en toute sécurité des fonctionnalités modernes de programmation système

Caractéristiques du langage

Exemple Hello World

  • Syntaxiquement proche du C
  • Les déclarations de fonction doivent utiliser explicitement le mot-clé fn
  • Les fonctions de la bibliothèque standard pour l’entrée/sortie sont puissantes et peuvent afficher directement divers types

Boucle foreach

  • Contrairement au C, la syntaxe foreach est prise en charge nativement
  • Pour itérer par référence, il faut écrire & devant le nom de variable (fonction avancée)
  • break et continue sont pris en charge, avec un fonctionnement proche du foreach d’autres langages

Boucle while

  • Avant C99, il n’était pas possible d’écrire une déclaration dans la condition d’un while, alors qu’en C3 c’est autorisé

enum et instruction switch

  • L’instruction switch prend en charge le break implicite (le mélange implicite/explicite pourra diviser selon les goûts)
  • Le mot-clé nextcase permet un passage explicite d’un case à l’autre (pratique pour implémenter une table de sauts)
  • Il devient possible de contrôler plus simplement des flux switch-case qui étaient plus complexes dans des langages comme Zig ou C

Mot-clé defer

  • À la sortie du scope, les instructions enregistrées avec defer sont exécutées en ordre inverse, ce qui garantit proprement le nettoyage des ressources
  • Utilisation de defer en combinaison avec catch et try pour contrôler les flux de gestion d’erreurs

struct et union

  • Les struct peuvent contenir des sous-struct/union nommées ou anonymes, ce qui facilite la conception de patterns de tagged union
  • La distinction entre les formes anonymes (avec duplication de champs de même nom) et les collisions de noms est strictement définie

Gestion des erreurs

  • Le symbole ? prend en charge les types optionnels, en unifiant erreurs et valeurs optionnelles pour plus de commodité
  • Le mot-clé catch permet de traiter l’état vide (sans Optional) ou une branche d’erreur
  • Contrairement à Rust ou Zig, la distinction entre erreur et valeur optionnelle est moins marquée (avantage : simplicité, inconvénient : intention moins explicite)
  • L’opérateur ! (rethrow) permet de propager une exception

Contrats (Contracts)

  • Les préconditions et postconditions d’une fonction (Require/Ensure) s’écrivent entre <* .. *> (vérification des conditions à la compilation)
  • L’analyse de fold à la compilation est aussi prise en charge (l’analyse statique n’est pas encore implémentée)

Méthodes de struct

  • Les méthodes associées sont définies via une notation de type + point (Foo.next), avec namespaces inclus (y compris pour les types primitifs)
  • Les méthodes sont autorisées sur tous les types, y compris struct, union et enum

Macros

  • Macros fondées sur l’évaluation à la compilation (mot-clé macro)
  • Les paramètres à la compilation sont gérés avec $, et # permet un passage avant évaluation
  • Style C (réduction des problèmes de macros imbriquées, accent mis sur la stabilité de l’AST, vérification avec préfixe @, etc.)
  • La réflexion de type et l’exécution à la compilation sont gérées par les macros

Propriétés de type

  • alignof, kindof, extnameof, sizeof, typeid, methodsof, has_tagof, tagof, is_eq, is_ordered, is_substruct
  • Adapté à la métaprogrammation et à la réflexion

Littéraux Base64/Hex

  • Les formes b64"..." et x"..." permettent de déclarer directement des séquences d’octets
  • Le macro intégré $embed peut les remplacer (dans les faits, leur usage semble peu fréquent)

Types primitifs

  • Nombreux types de base : int, uint, char (toujours unsigned), bool, float, int128/uint128, etc.
  • Types distincts pour les pointeurs et tailles, comme iptr, uptr, isz, usz (un peu moins intuitifs)
  • Contrairement au C, la taille en bits est garantie

Divers

  • Large ensemble de fonctionnalités, dont la surcharge d’opérateurs, le sous-typage de structures, les génériques, le runtime dispatch, le type any et les bitstructs

Pratique : retour d’expérience avec C3

Installation de C3

  • Deux méthodes sont prises en charge : binaire précompilé depuis le site officiel ou compilation directe depuis les sources
  • Installation de LLVM et LLD requise (en cas de problème de linkage, utiliser les flags CMake -DLLVM_DIR et -DLLD_DIR)
  • Certaines distributions n’incluant pas les bibliothèques LLD, il est recommandé de télécharger directement les binaires
  • Le compilateur C3 dépend de libtinfo

Création de projet

  • La commande c3c init génère une structure de dossiers standard (LICENSE/README.md/project.json/src, etc.)
  • Mise en place du projet avec Bluild, cibles de build, configuration des sources, etc. (proche de Zig et Cargo)
  • Le fichier main.c3 par défaut est très concis (avis : bien adapté aux nouveaux utilisateurs)

Créer une calculatrice

Conception et objectif

  • Implémenter un parseur à descente récursive (Recursive Descent Parser) et la logique centrale d’une calculatrice afin de pratiquer diverses syntaxes de C3 : fonctions, entrées/sorties, gestion mémoire, boucles, etc.
  • Objectif : identifier directement les points forts et les aspects moins confortables du langage, notamment en matière d’intuitivité syntaxique et de productivité en situation réelle

Traitement des entrées

  • @pool permet d’utiliser un allocateur temporaire (tmem), avec libération automatique de la mémoire à la fin du scope (arena allocator)
  • Le langage propose tmem (temporaire) et mem (général) comme modes de gestion mémoire standard, avec un modèle de passage d’allocateur par fonction (mélange des atouts de Zig et du C)
  • La fonction main doit obligatoirement déclarer une valeur de retour (contrainte imposée par le compilateur)
  • Les fonctions dont la valeur de retour peut être ignorée sont annotées avec l’attribut @maydiscard (pour éviter un oubli abusif)

Implémentation du tokenizer

  • Décomposition de l’entrée utilisateur en liste de tokens
  • Utilisation de diverses structures de contrôle de la bibliothèque standard de C3, comme List, la syntaxe foreach et switch-case (nextcase, combinaison de break implicite/explicite)
  • Une certaine confusion existe autour de la syntaxe des slices (indices aux deux extrémités inclus) et des slices de longueur 0 (une syntaxe distincte de spécification de longueur existe)
  • La combinaison d’allocateurs temporaires et généraux rend la gestion mémoire transparente et flexible, avec un résultat supérieur à certains autres langages comme Rust

Implémentation du parseur

  • Retour d’expérience sur l’écriture directe du parseur (omis)

Conclusion et avis d’ensemble

  • C3 cherche le point de rencontre entre les langages système traditionnels et une conception moderne
  • Conçu en étudiant Zig, Rust et C, il vise à concilier performances et stabilité du code
  • Se distinguent notamment la modularité, la gestion sûre de la mémoire/des erreurs/des contrats, la métaprogrammation puissante et un système de build intuitif
  • La courbe d’apprentissage reste progressive pour quelqu’un ayant déjà de l’expérience en C
  • L’écosystème encore immature (serveur de langage, IDE, etc.) ainsi que certaines préférences de syntaxe restent à améliorer
  • Il mérite l’attention comme langage alternatif de nouvelle génération pour le développement bas niveau et système

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.