Initialisation et Finalisation d'unités pour la nouvelle chaîne d'outils C++
Remonter à Packages pour la nouvelle chaîne d'outils C++
Les dépendances d'initialisation et de finalisation des unités peuvent entraîner des problèmes. Comme il s'agit d'un problème récurrent, RAD Studio a amélioré considérablement la gestion des packages afin d'assurer un comportement cohérent lors de la liaison statique et dynamique des packages. Cette page explique ceci en détail.
Les packages sont toujours disponibles en deux versions, la seconde étant une bibliothèque statique standard. La liaison statique d'un package consiste à établir une liaison avec cette bibliothèque statique standard, ce qui signifie que le contenu du package est intégré au binaire lié. Vous n'avez donc pas besoin de redistribuer des fichiers supplémentaires avec ce package.
Dans Delphi, les unités ont des interdépendances. Cela signifie qu'une unité utilise une autre unité ainsi que les types définis dans cette dernière. Des dépendances d'initialisation et de finalisation peuvent apparaître lorsqu'une unité est initialisée, autrement dit dans l'une des situations suivantes :
- Mots-clés Initialization et finalization.
- Constructeurs ou destructeurs de classes.
Par exemple, lors de l'initialisation d'un objet qui dépend d'une autre instance d'objet ou d'une allocation située dans une autre unité, ce comportement provoque l'initialisation des unités basée sur un graphe de mappage de dépendances. Il n'existe pas de solution adaptée aux graphes de dépendances lorsqu'ils sont circulaires. Dans ce cas, des bogues sont susceptibles de se produire.
Ainsi, il est fréquent que les problèmes d'initialisation entraînent des violations d'accès au démarrage ou lors du chargement d'un package, notamment lorsqu'une unité utilisée n'a pas encore été initialisée. RAD Studio empêche et limite ces problèmes.
Gardez à l'esprit que seules les unités utilisées sont initialisées et finalisées que vous utilisiez la liaison dynamique ou la liaison statique. Contrairement aux chaînes d'outils précédentes (Clang 5 et versions antérieures, et classique), les références d'unité circulaires sont gérées, ainsi que les unités faiblement packagées.
Comment les packages sont-ils initialisés dynamiquement ?
Lorsqu'un package est chargé dynamiquement, ses unités sont initialisées récursivement avec un compteur. Cela garantit que lorsqu'un second package utilisant les mêmes unités est chargé, celles-ci ne sont pas initialisées deux fois et ne sont finalisées qu'après le déchargement du dernier package.
Les packages peuvent être dynamiquement initialisés de deux manières :
- Via la bibliothèque d'importation de packages, le fichier
.BPI. C'est le système habituel. C'est aussi comme cela que la liaison automatique fonctionne.
Cette méthode utilise le système d'initialisation par enregistrement et initialise donc uniquement les unités utilisées. Des différences dans l'ordre d'initialisation des unités peuvent apparaître si un package dépend de deux emplacements dans la même application.
Supposons le scénario suivant :App.exedépend deA.bpletB.bplB.bpldépend deA.bpl.
Au chargement de
App.exe, les unités qu'il utilise dansA.bplainsi que toutes les unités interdépendantes, sont initialisées. Cela signifie que l'intégralité du graphe de dépendances des unités est initialisée.
Cependant, les unités de
A.bpldont dépendB.bplne sont pas initialisées. LorsqueApp.exechargeB.bpl, ses dépendances sont initialisées depuisA.bpl. Si elles contiennent des unités deA.bplayant déjà été initialisées, ces unités ne seront pas initialisées une deuxième fois.
Cela signifie que même si chaque graphe ou ensemble d'unités est initialisé correctement, il est possible que le chargement de
A.bplparApp.exeavantB.bplprovoque un changement de l'ordre de l'initialisation des unités de A.bpl par rapport à celui obtenu siApp.exedépendait uniquement deB.bplou le chargement s'était effectué dans l'ordreB.bpl(->A.bpl) puisA.bpl. Ce comportement n'impacte normalement pas la stabilité, car toutes les unités utilisées sont initialisées dans l'ordre correct défini par les enregistrements d'initialisation. - Via SysUtils::LoadPackage. Avec cette méthode, les packages sont chargés dynamiquement.
Toutes les unités dépendantes du package sont initialisées récursivement, qu'elles soient utilisées ou non, car il est impossible de savoir lesquelles sont utilisées par le module de chargement.
Lorsqu'un package est chargé, ses dépendances le sont aussi. Cela signifie que si vous chargez un packagePackage1.bplqui dépend dertl.bplen le liant dynamiquement ou en utilisantLoadPackage, ni vous niPackage1.bpln'avez besoin d'appelerLoadPackage(“rtlXXX.bpl”), car le chargement dePackage1.bplprovoque le chargement et l'initialisation de toutes les unités de package dépendantes utilisées parPackage1.bpl. Cela inclut la gestion des dépendances des unités contenues dans d'autres packages, commertl.bpl.
Une application peut mélanger les deux méthodes de chargement dynamique des packages, mais elle ne doit pas mélanger liaison statique et liaison dynamique.
L'initialisation et la finalisation des unités deviennent plus complexes à mesure que les niveaux de dépendances augmentent au sein d'un package et entre les packages. Des tests approfondis sont nécessaires pour tester les dépendances complexes.
Lors d'une liaison statique, seules les unités utilisées sont initialisées. Le lieur supprime les objets inutilisés, ce qui signifie que les unités non référencées ou non utilisées n'existent pas dans le binaire final.
Voir aussi
- Chargement des packages dans une application
- Chargement des packages avec la fonction LoadPackage
- RTTI de Delphi et C++Builder
- Utilisation de la directive de package faible