Frage

In C ++, weiß ich, dass der Compiler statische Objekte in beliebiger Reihenfolge initialisieren können wählen, dass es (vorbehaltlich einigen Einschränkungen) wählt, und dass in der Regel können Sie nicht wählen oder die statische Initialisierungsreihenfolge bestimmen.

Sobald jedoch ein Programm kompiliert wurde, hat der Compiler eine Entscheidung darüber gemacht haben, was um in diese Objekte zu initialisieren. Gibt es eine Möglichkeit, aus einem kompilierten Programm mit Debug-Symbolen, um zu bestimmen, in welcher Reihenfolge statische Konstruktoren genannt werden?

Der Kontext ist dies: Ich habe ein umfangreiches Programm, das plötzlich vor dem Haupt wird Speicherzugriffsfehler (), wenn sie unter einem neuen Werkzeugkette eingebaut ist. Entweder ist dies ein statisches Initialisierungsreihenfolge Problem, oder es ist etwas falsch mit einem der Bibliotheken, die es geladen ist. Allerdings, wenn ich mit gdb debuggen, wird die Crash-Position einfach als Ausgang Adresse ohne symbolische Information oder Backtrace berichtet. Ich möchte, welche diese beiden Probleme entscheiden, es ist durch einen Haltepunkt an dem Konstruktor des ersten statisch initialisierten Objekts platzieren, aber ich weiß nicht, wie zu sagen, welches Objekt das ist.

War es hilfreich?

Lösung

Matthew Wilson bietet eine Möglichkeit, diese Frage in diesem Abschnitt (Safari Books Online zu beantworten Abonnement erforderlich) von Imperfect C ++ . (Good Buch, übrigens.) Zusammenfassend schafft er einen CUTrace.h-Header, der eine statische Instanz einer Klasse erstellt, die die Dateinamen der einschließlich Quelldatei druckt (mit dem Nicht-Standard-Präprozessormakro __BASE_FILE__), wenn erstellt, dann schließt er CUTrace.h in jede Quelldatei.

Dies erfordert eine erneute Kompilierung, aber die # include „CUTrace.h“ kann einfach über ein Skript hinzugefügt und entfernt werden, so sollte es nicht allzu schwierig sein, einzurichten.

Andere Tipps

In G ++ auf Linux, statische und Destruktor Ordnung wird durch Funktionszeiger in den .ctors und .dtors Abschnitten bestimmt. Beachten Sie, dass mit ausreichend Debugging zur Verfügung, die Sie tatsächlich einen Backtrace bekommen:

(gdb) bt
#0  0xb7fe3402 in __kernel_vsyscall ()
#1  0xb7d59680 in *__GI_raise (sig=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2  0xb7d5cd68 in *__GI_abort () at abort.c:88
#3  0x08048477 in foo::foo() ()
#4  0x0804844e in __static_initialization_and_destruction_0(int, int) ()
#5  0x0804846a in global constructors keyed to foo_inst ()
#6  0x0804850d in __do_global_ctors_aux ()
#7  0x08048318 in _init ()
#8  0x080484a9 in __libc_csu_init ()
#9  0xb7d4470c in __libc_start_main (main=0x8048414 <main>, argc=1,
    ubp_av=0xbfffcbc4, init=0x8048490 <__libc_csu_init>,
    fini=0x8048480 <__libc_csu_fini>, rtld_fini=0xb7ff2820 <_dl_fini>,
    stack_end=0xbfffcbbc) at libc-start.c:181
#10 0x08048381 in _start () at ../sysdeps/i386/elf/start.S:119

Dies ist mit Debug-Symbole für libc und libstdc ++ installiert. Wie Sie sehen können, trat der Absturz hier in den foo :: foo () Konstruktor für das statische Objekt foo_inst.

Wenn Sie in den Initialisierungsprozess zu durchbrechen wollen, müssen Sie dann einen Haltepunkt auf __do_global_ctors_aux setzen konnte und Schritt durch seine Demontage, nehme ich an. Oder einfach nur warten, bis es den Backtrace wie die oben zu erhalten zum Absturz bringen.

Könnte initialisieren Sie Dummy-Variablen in dem statischen Raum, und Punkte auf diese Funktionsaufrufe brechen?

extern "C" int breakOnMe () { return 0 };

int break1 = breakOnMe ();
float pi = 3.1415;
int break2 = breakOnMe ();
myClass x = myClass (1, 2, 3);

Dann in gdb Laufe break breakOnMe, bevor Sie das Programm ausführen. Das sollte sich nach den statischen Initialisierungen vor jedem gdb Pause machen.

ich denke, das sollte funktionieren .. Ich bin ein wenig rostig auf gdbbing.

Sie können den Auftrag finden die TUs initialisiert werden mit Hilfe von Vorlagen, wie durch diese Frage . Es erfordert ein kleines Stück Code ändern zu jedem der TUs, die Sie interessiert sind:

// order.h
//

#ifndef INCLUDED_ORDER
#define INCLUDED_ORDER

#include <iostream>

inline int showCountAndFile (const char * file)
{
  static int cnt = 0;
  std::cout << file << ": " << cnt << std::endl;
  ++cnt;
  return cnt;
}

template <int & i>
class A {
  static int j;
};

template <int & i>
int A<i>::j = showCountAndFile (SRC_FILE);

namespace
{
  int dummyGlobal;
}
template class A<dummyGlobal>;

#endif

Die Grundidee ist, dass jede TU eine andere eindeutige Adresse für dummyGlobal haben wird und so die Vorlage eine andere Instanziierung in jeder TU hat. Die Initialisierung der statischen Member-Ergebnisse in dem Aufruf von „showCountAndFile“, die dann ausdruckt SRC_FILE (in der TU) und den aktuellen Wert von cnt die daher den Auftrag zeigen.

Sie würde es wie folgt verwenden:

static const char * SRC_FILE=__FILE__;
#include "order.h"

int main ()
{
}

g ++ bietet etwas Hilfe mit diesem.
Es ist nicht tragbar, aber ich bin an dieser Stelle sicher, dass nicht Ihr Hauptproblem ist.

http://gcc.gnu.org/onlinedocs /gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

Eigentlich durch die Verwendung von Singletons Sie die Reihenfolge der Initialisierung der globalen / statischer Objekte steuern können sehr effektiv in C ++.

Zum Beispiel, sagen Sie:

class Abc
{
public:
    void foo();
};

und ein entsprechendes Objekt im globalen Bereich definiert:

Abc abc;

Dann haben Sie eine Klasse:

class Def
{
public:
    Def()
    {
        abc.foo();
    }
};

, die hat auch ein Objekt im globalen Bereich definiert:

Def def;

In dieser Situation müssen Sie nicht die Kontrolle über die Initialisierung Ordnung und wenn def zuerst initialisiert wird, dann ist es wahrscheinlich, dass Ihr Programm zum Absturz bringen, weil es den foo () -Methode auf einem Abc ruft die initialisiert wurde noch nicht.

Die Lösung ist eine Funktion im globalen Bereich zu haben, etwas tun, wie folgt aus:

Abc& abc()
{
    static Abc a;
    return a;
}

und dann würde Def in etwa so aussehen:

class Def
{
public:
    Def()
    {
        abc().foo();
    }
};

Auf diese Weise wird abc immer garantiert initialisiert werden, bevor es verwendet wird, da diese beim ersten Aufruf der abc () Funktion passieren werden. Ebenso sollten Sie das gleiche mit Def globalem Objekt tun, um es gewohnt entweder unerwartete Initialisierung Abhängigkeiten zu machen.

Def& def()
{
    static Def d;
    return d;
}

Wenn Sie genau die Reihenfolge der Initialisierung zusätzlich zu einfach sicherstellen, dass alles initialisiert steuern müssen, bevor sie verwendet wird, setzen alle globalen Objekte im globalen Singleton wie folgt.

struct Global
{
    Abc abc;
    Def def;
};

Global& global()
{
    static Global g;
    return g;
}

Und Verweise auf diese Artikel wie folgt:

//..some code
global().abc.foo();
//..more code here
global().def.bar();

Unabhängig von dem man Anrufe zuerst bekommt, C ++ Mitglied Initialisierung Regeln wird sichergestellt, dass die abc und def Objekte in der Reihenfolge initialisiert werden sie in der globalen Klasse definiert sind.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top