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
la réponse finale semble être
__atsortingbute__ ((init_priority (101)))
à cette instance, 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.