Explizite Kontrolle der Initialisierung und Zerstörung globaler VariablenC++

Programme in C++. Entwicklerforum
Anonymous
 Explizite Kontrolle der Initialisierung und Zerstörung globaler Variablen

Post by Anonymous »

Ich arbeite in einer Mikrocontroller-/eingebetteten Umgebung, die auf globalen Variablen basiert. Einige dieser globalen Variablen haben nicht triviale Konstruktoren und Destruktoren und leiden unter dem Problem statisches Initialisierungsreihenfolge-Fiasko. Ich muss die Reihenfolge klar definieren und in meiner Kontrolle haben. Eine übliche Lösung hierfür ist die Verwendung einer Funktion mit einer darin definierten statischen Variablen:

Code: Select all

Type& GetObject()
{
static Type g_Object;
return g_Object;
}
Bei der ersten Verwendung von GetObject() wird die lokale statische Variable erstellt und kann daher Teil einer genau definierten Initialisierungsreihenfolge sein:

Code: Select all

int main()
{
...
GetObject1();
GetObject2();
...
}
Aber was ist, wenn ich möchte, dass die Zerstörung an einem bestimmten Punkt und auch in einer genau definierten Reihenfolge erfolgt? (In diesem Fall direkt vor dem Wechsel in den Abschaltmodus des Mikrocontrollers.)
Wenn das Programm in einer gehosteten Umgebung normal beendet werden würde, würde die Zerstörung in der umgekehrten Reihenfolge der Initialisierung erfolgen, was in Ordnung ist. Da das Programm in diesem Fall jedoch nicht normal beendet wird, trifft dies nicht zu. Vielleicht kann std::exit verwendet werden (habe ich noch nicht versucht), aber es führt auch zusätzliche Vorgänge aus, die nicht benötigt werden (z. B. das Leeren von C-Streams).
Potenzielle Lösung
Diese Lösung beinhaltet einen Wrapper-Union-Typ, der darauf angewiesen ist, dass der Benutzer eine explizite Initialisierung und Zerstörung durchführt. Der Konstruktor initialisiert den trivialen Typ-Member, wodurch er sicher in jeder globalen Reihenfolge initialisiert werden kann, und der Destruktor tut nichts.
Unter der Haube verwendet er die Platzierung „new“ zum Initialisieren und einen Aufruf des Destruktors des zugrunde liegenden Typs, wenn er zerstört wird.
Es liegt am Benutzer, den Wrapper korrekt zu verwenden – andernfalls undefiniertes Verhalten.

Code: Select all

#include 
#include 

template
union Lazy_t
{
public:
inline Lazy_t() : m_uninitialized{} {}
inline ~Lazy_t() {}

template
inline void Initialize(Args&&... args) { ::new (&m_initialized) T(std::forward(args)...); }
inline void Destroy()                  { m_initialized.~T(); }

inline T&       operator* ()       { return m_initialized; }
inline const T& operator* () const { return m_initialized; }
inline T*       operator->()       { return &m_initialized; }
inline const T* operator->() const { return &m_initialized; }

private:
struct Uninitialized_t {};

Uninitialized_t m_uninitialized;
T m_initialized;
};
Es kann auf folgende Weise verwendet werden:

Code: Select all

struct NonTrivial_t
{
NonTrivial_t() : m_value{} { m_value = new int{}; }
~NonTrivial_t() { delete m_value; }

int* m_value;
};

Lazy_t g_Object1;
Lazy_t g_Object2;

void Main()
{
// ... microcontroller initialization ...

g_Object1.Initialize();
g_Object2.Initialize();

// ...
*g_Object1->m_value = 1;
*g_Object2->m_value = 2;
// ...

g_Object2.Destroy();
g_Object1.Destroy();

// ... microcontroller de-initialization and enter shutdown ...
}
Gibt es Probleme mit dieser Lösung?

Anhang: Lazy Type (Wrapper) mit Sicherheitsprüfungen:

Code: Select all

#include 

template
class SafeLazy_t
{
public:
inline SafeLazy_t()
: m_lazy{}
#ifndef NDEBUG
, m_initialized{}
#endif
{}

inline ~SafeLazy_t()
{
#ifndef NDEBUG
assert(!m_initialized);
#endif
}

template
inline void Initialize(Args&&...  args)
{
#ifndef NDEBUG
assert(!m_initialized);
#endif
m_lazy.Initialize(std::forward(args)...);
#ifndef NDEBUG
m_initialized = true;
#endif
}

inline void Destroy()
{
#ifndef NDEBUG
assert(m_initialized);
#endif
m_lazy.Destroy();
#ifndef NDEBUG
m_initialized = false;
#endif
}

inline T& operator*()
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return *m_lazy;
}

inline const T& operator*() const
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return *m_lazy;
}

inline InnerLazy_t& operator->()
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return m_lazy;
}

inline const InnerLazy_t& operator->() const
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return m_lazy;
}

private:
Lazy_t m_lazy;
#ifndef NDEBUG
bool m_initialized;
#endif
};

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post