Comment créer / construire une instance de classe plus tôt que toute autre variable globale ou statique?

Comment créer une instance d’une classe particulière antérieure à main (), antérieure à toute autre instance (y compris les variables statiques et globales, y compris les bibliothèques statiques et dynamics (y compris les bibliothèques fournies par des tiers))?


Je m’occupe d’un projet avec un certain nombre de classes qui, lors de la construction, peuvent provoquer une erreur, par exemple accéder à un pointeur NULL. Une telle erreur provoque l’envoi d’un signal à l’application. J’ai un gestionnaire de signal qui attrape les signaux, montre la trace de la stack du thread incriminé et appelle le gestionnaire de signal par défaut qui génère le vidage du kernel, etc.
Cependant, certaines de ces instances provoquant des erreurs sont créées en tant que variables globales et variables statiques des classes. C’est-à-dire qu’ils sont construits et provoquent un signal plus tôt que main () est entré.

Pour capturer de tels signaux, je dois enregistrer mon gestionnaire de signaux plus tôt que dans main (), c’est-à-dire que je dois créer une instance (qui enregistrera le gestionnaire de signaux) également en tant que variable globale ou statique. une instance est créée / construite plus tôt que toute autre instance.

Comment faire cela?


Pour enregistrer un gestionnaire de signal, j’utilise sigaction () .
Pour afficher la trace de la stack, j’utilise backtrace () , backtrace_symbols () , abi :: __ cxa_demangle () .

Le standard C ++ ne permet pas de commander les initialiseurs entre les unités de traduction, contrairement à gcc. Exemple de https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Atsortingbutes.html :

Some_Class A __atsortingbute__((init_priority (2000))); 

La valeur la plus basse (priorité la plus élevée) est 101.

L’ordre d’appeler des constructeurs statiques dépend de leur ordre dans un fichier source, de l’ordre de liaison et de l’ordre de chargement des bibliothèques partagées. La manière habituelle de faire ce dont vous avez besoin est de créer un fichier séparé avec votre fonction d’enregistrement et de vous assurer que l’object correspondant apparaît comme le premier à la ligne de commande de l’éditeur de liens. Il ne devrait pas faire partie d’une bibliothèque partagée ou statique.

ie premier.c

 static int first_function() { // register your signals ... return 0; } static int status = first_function(); gcc -c first.c -o first.o gcc -o myexec first.o ... all other files 

Vous pouvez créer un petit projet ou un exécutable en dehors de votre application principale qui est compilé et construit séparément dans sa propre solution. Cependant, cette petite application aurait besoin d’un argument de ligne de commande requirejs pour fonctionner avec votre application principale que vous souhaitez tester. L’argument de la ligne de commande serait le chemin d’access au répertoire ou au chemin du dossier de votre projet principal contenant un fichier d’en-tête compatible avec cette application externe. Ensuite, en fonction de l’IDE que vous utilisez, vous devrez effectuer certaines configurations de votre IDE pour définir les chemins appropriés.

Par exemple, si vous travaillez dans MS Visual Studio. Vous pouvez cliquer avec le bouton droit sur votre projet de démarrage et sélectionner les propriétés, puis sur la page de propriétés située à gauche, une section intitulée Build Events Si vous développez cette section, vous avez trois possibilités: Pre-Build Event Post-Build Event Ces trois champs ont les mêmes champs sur le côté droit pour leurs parameters: Command Line où vous devez définir vos chemins, Description tout ce que vous voulez utiliser pour décrire la commande d’événement de génération, et Use In Build qui est fondamentalement une bascule booléenne de Y/N

Désormais, les autres IDE seront différents dans leur configuration et les options disponibles, mais quelque chose de cette nature pourrait convenir à vos besoins. Pardonnez-moi l’exemple ci-dessus qui utilise Microsoft Visual Studio car je ne suis pas familier avec Linux. Cependant, je crois que quelque chose de similaire peut être fait; il suffit de connaître votre compilateur, votre éditeur de liens et votre débogueur.

Il peut y avoir une solution en utilisant des variables de calcul statiques.

 class A; A & GetA() { static A internal_a; return internal_a; } ... class B { public: B() { auto &a = GetA(); a.DoSomething(); } } ... B global_b; ... 

Chaque fois que le constructeur de B est appelé, en appelant ‘GetA’, la variable de fonction statique ‘internal_a’ est initialisée avant d’être utilisée.

Voir aussi Quelle est la durée de vie d’une variable statique dans une fonction C ++?

Cette technique est également décrite dans le livre “Effective C ++” de Scott Meyers.

Il est impossible de le faire malheureusement, sauf si vous avez access à la source (ce qui est exclu car vous avez des bibliothèques tierces).

Si vous faites quelque chose comme ça:

 // Foo.h inline fooGlobal() { static Foo f; return f; } static auto& foo = fooGlobal(); 

Ensuite, vous pouvez garantir que tout fichier incluant Foo.h verra foo initialisé avant les globaux de ce fichier. Si vos bibliothèques tierces ne définissent que les globaux dans les fichiers d’en-tête (en utilisant une astuce similaire à celle ci-dessus pour empêcher l’obtention de plusieurs globales), vous pouvez vous assurer que tous les TU incluent Foo.h avant l’en-tête tiers. Mais si la bibliothèque tierce possède des globales définies dans le fichier .cpp, vous ne pouvez absolument rien faire, en termes de C ++ pur, en évitant de pirater ce code.

Maintenant, il y a une chose que vous pouvez faire, si vous êtes vraiment désespéré: vous pouvez définir votre global dans une bibliothèque partagée et utiliser l’astuce LD_PRELOAD pour vous assurer que votre bibliothèque partagée est chargée avant toute chose: Qu’est-ce que l’astuce LD_PRELOAD? . C’est assez pirate mais si c’est un binary dont vous avez le contrôle total (c’est-à-dire que vous ne le dissortingbuez pas aux clients, peut-être juste un serveur que vous utilisez vous-même), cela peut être suffisant.

(Fournir une réponse temporaire à ma propre question)
Basé sur

  • cette réponse par @Serge,
  • cette réponse par @ToddFleming,
  • et ceci ma propre observation

la réponse finale semble être

  • créer une instance globale dans un fichier .CPP,
  • pour append __atsortingbute__ ((init_priority (101))) à cette instance,
  • pour lister ce fichier en premier dans la liste des éditeurs de liens pour une bibliothèque dynamic / partagée,
  • construire une bibliothèque dynamic / partagée,
  • chargez cette bibliothèque en premier lieu par toutes les autres bibliothèques dynamics / partagées.

Cette solution n’a pas été vérifiée dans son intégralité (j’espère que je vérifierai un peu plus tard).


Si vous décidez de voter pour cette réponse, alors plutôt (ou du moins en plus), mieux voter pour les autres réponses que j’ai mentionnées ci-dessus.