L'objectif principal de Beef est de fournir une expérience de développement fluide et agréable pour les applications en temps réel hautes performances telles que les jeux vidéo, avec des fonctionnalités de bas niveau qui le rendent adapté au développement de moteur, combiné à une ergonomie de haut niveau adaptée au développement de code de jeu.
Le langage a d'abord été publié le 29 septembre en version 0.42.0 et a reçu une mise à jour le 31 décembre qui a apporté :
- une amélioration des capacités de compilation croisée ;
- un ajout de cibles de build macOS, iOS et Android ;
- des optimisations pour le GC d'allocateur de débogage ;
- une prise en charge supplémentaire des contraintes de l'opérateur ;
- un ajout d'attributs de remplacement d'alignement d'allocations ;
- une prise en charge étendue de la liste de capture lambda pour inclure des noms de variables spécifiques ;
- des capacités annulables améliorées, y compris les opérateurs.
Beef permettrait de mélanger en toute sécurité différents niveaux d'optimisation sur un niveau par type ou par méthode, permettant au code critique de performance d'être exécuté à la vitesse maximale sans affecter la débogabilité du reste de l'application.
La gestion de la mémoire dans Beef est manuelle et inclut un support de première classe pour les allocateurs personnalisés. Des précautions ont été prises pour réduire le fardeau de la gestion manuelle de la mémoire grâce à l'ergonomie du langage et aux sécurités d'exécution - Beef pourrait détecter les fuites de mémoire en temps réel et offrirait une protection garantie contre les erreurs d'utilisation après libération et de double suppression. Comme avec la plupart des fonctions de sécurité de Beef, ces sécurités de mémoire peuvent être désactivées dans les versions pour une performance maximale.
Le Beef IDE prend en charge des fonctionnalités de productivité telles que la saisie semi-automatique, les correctifs, le reformatage, les outils de refactorisation, l'inspection de type, la compilation de code d'exécution (échange de code à chaud) et un profileur intégré. Le débogueur polyvalent de l'EDI serait capable de déboguer des applications natives écrites dans n'importe quel langage, et est destiné à être un débogueur autonome complet, même pour les développeurs C / C ++ purs qui souhaitent une alternative au débogage de Visual Studio.
Modèle de compilation
Le contexte de compilation Beef est un espace de travail, qui se compose de plusieurs projets. Un projet peut être soit une bibliothèque, soit produire un binaire tel qu'un exécutable ou une DLL. Les sources sont analysées, transmises via un préprocesseur limité, compilées et une collection de fichiers objet est créée pour les types et méthodes référencés, qui sont ensuite liés aux binaires cibles. Le modèle de compilation à l'échelle de l'espace de travail permet aux paramètres par espace de travail d'affecter les compilations de groupes spécifiques de méthodes ou de types, en modifiant le préprocesseur et les paramètres de compilation (par exemple: niveau d'optimisation) du code même lorsqu'il est contenu dans des bibliothèques tierces référencées.
La compilation incrémentielle est prise en charge, avec un graphe de dépendances reconstruisant uniquement les objets potentiellement affectés et avec un cache principal pour éviter de reconstruire des objets sans modifications fonctionnelles. La compilation incrémentielle peut être désactivée pour créer des versions reproductibles.
Beef prend en charge plusieurs backends de compilateur, y compris LLVM et un backend de « débogage amélioré » (Og +) personnalisé qui effectue certaines optimisations de code qui n'ont pas d'incidence négative sur le débogage et a quelques améliorations dans les informations de débogage émises sur LLVM.
Plusieurs éditeurs de liens sont pris en charge, y compris les éditeurs de liens système et l'éditeur de liens LLVM qui peuvent être utilisés pour les générations optimisées au moment de la liaison (LLVM LTO / ThinLTO).
Fonctionnalités de sécurité
Beef prend en charge une variété de fonctionnalités de sécurité optionnelles, dont beaucoup peuvent être désactivées pour des groupes de code spécifiés pour les versions de « sécurité mixte ». Par défaut, les vérifications suivantes sont activées pour tout le code dans les versions de débogage et elles sont désactivées dans les versions de version.
Vérification des limites
La vérification des limites est implémentée dans la bibliothèque standard pour les tableaux, les collections, les étendues et les chaînes. Dans de nombreux cas, ceux-ci sont implémentés en ayant un accesseur [Checked] qui effectue des vérifications des limites et un autre accesseur [Unchecked] qui ne vérifie pas les limites. Cela permet de vérifier les limites sur le site d'appel plutôt que d'être déterminé à l'échelle de la collection.
Code : | Sélectionner tout |
1 2 | // Désactiver la vérification des limites pour cet index spécifique int val = arr[[Unchecked]i]; |
Code : | Sélectionner tout |
1 2 3 4 5 6 | // Ne fait aucune vérification dans cette méthode [DisableChecks] void Calculate() { int val = arr[i]; } |
Les transtypages d'objets explicites vers un type dérivé non valide seront interceptés lors de l'exécution.
Fuites de mémoire
Les fuites peuvent être détectées en temps réel avec le gestionnaire de mémoire de débogage. La mémoire accessible sera tracée en continu lors de l'exécution et la mémoire qui n'est plus accessible, mais qui n'a pas été correctement libérée sera immédiatement signalée comme une fuite, avec l'emplacement du code où l'allocation s'est produite. La profondeur de trace de pile pour ce suivi d'allocation est réglable.
Double libération / utilisation après libération
Lorsque le gestionnaire de mémoire de débogage est activé, les objets dont la libération a été demandée seront marqués comme « libérés », mais la mémoire ne sera pas récupérée physiquement tant qu’il n’y aura plus de références à la mémoire qu’elle occupe. Toute tentative d'utilisation de la mémoire après qu'elle a été marquée comme libérée est garantie d'échouer immédiatement, et la valeur de cet objet libéré et sa trace de pile d'allocations seront valides et visibles dans le débogueur.
Gestion de la mémoire
Allocation de la mémoire
Les allocations peuvent être placées sur la pile, l'allocateur global ou un allocateur personnalisé. Les allocations de pile utilisent le mot clé «scope», qui peut spécifier une étendue de l'étendue actuelle (c.-à-d.: Bloc de code) à l'étendue de la méthode entière (même dans une boucle).
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | static void Test(StreamReader fs) { let strList = scope List<String>(); for (let line in fs.Lines) { /* The scope of this string is the whole method */ let lineStr = scope:: String(line); strList.Add(lineStr); } strList.Sort(); } static void Test(StreamReader fs) { Sort: { let strList = scope List<String>(); for (let line in fs.Lines) { /* The scope of this string is the "Sort" scope */ let lineStr = scope:Sort String(line); strList.Add(lineStr); } strList.Sort(); } } |
Les allocations via l'allocateur global utilisent le mot clé « new ».
Code : | Sélectionner tout |
1 2 3 4 | String AllocGlobalString(int len) { return new String(len); } |
Code : | Sélectionner tout |
1 2 3 4 | String AllocCustomString(int len) { return new:customAllocator String(len); } |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct ArenaAlloc { public void* Alloc(int size, int align) { return Internal.StdMalloc(size); } public void* AllocTyped(Type type, int size, int align) { void* data = Alloc(size, align); if (type.HasDestructor) MarkRequiresDeletion(data); } public void Free(void* ptr) { Internal.StdFree(ptr); } } |
Les allocations personnalisées peuvent également être allouées via des mixins, ce qui peut même permettre une allocation conditionnelle sur la pile. Le mixage ScopedAlloc, par exemple, effectuera de petites allocations sur la pile et de gros objets sur le tas.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | static mixin ScopedAlloc(int size, int align) { void* data; if (size <= 128) { data = scope:mixin [Align(align)] uint8[size]* { ? }; } else { data = new [Align(align)] uint8[size]* { ? }; defer:mixin delete data; } data } void ReadString(int reserveLen) { String str = new:ScopedAlloc! String(reserveLen); UseString(str); } |
L'allocateur global est sélectionné pour chaque espace de travail. Par défaut, les allocateurs CRT malloc / free sont utilisés, mais n'importe quel allocateur global de style C peut être utilisé, comme TCMalloc ou JEMalloc. De plus, Beef contient un allocateur de débogage spécial qui permet des fonctionnalités telles que la vérification des fuites en temps réel et la compilation à chaud.
Les allocations Beef sont de style C en ce sens qu'elles ne sont pas délocalisables et qu'il n'y a pas de garbage collector.
Libérer de la mémoire
Les allocations de portée sont automatiquement débloquées à la fin de scope, mais les allocations manuelles doivent être débloquées manuellement avec le mot-clé «delete». De même que pour les allocations d'allocateurs personnalisées, la suppression peut spécifier une cible d'allocateur personnalisée pour libérer de la mémoire à partir d'allocateurs personnalisés.
Source : dépôt du langage Beef
Et vous ?
Que pensez-vous de Beef dans l'absolu ? Allez-vous chercher à en savoir un peu plus sur ce langage ?
Quels sont les éléments qui peuvent vous pousser à apprendre un nouveau langage de programmation ?
Le plus souvent le faites-vous pour des projets personnels ou des projets dans votre entreprise ?
En général, vous orientez-vous vers un langage de programmation mainstream ou êtes-vous parfois curieux de découvrir d'autres langages qui ne sont pas forcément autant populaires, mais qui font bien leur travail ?
Que pensez-vous de la prolifération des nouveaux langages de programmation ? Y en a-t-il trop ou pas assez selon vous ?
À quel moment est-ce pertinent selon vous de créer un nouveau langage de programmation ?
Voir aussi :
Programmation : le C « langage de l'année 2019 » devant C# et Python, d'après les chiffres de la première édition de l'index TIOBE pour l'année 2020
Joyeux anniversaire ANSI C : cela fait déjà 30 ans que le langage de programmation C a été normalisé, petit tour d'horizon sur son évolution
Pourquoi la programmation fonctionnelle n'est-elle pas la norme de l'industrie du code ? L'auteur de « Elm in action » s'exprime, « c'est une question de temps avant que la POO soit détrônée »
Quels sont les meilleurs langages de programmation à apprendre en 2020 ? Voici un classement de ces langages selon le cas d'utilisation