Ich habe es immer wieder mit langen Listen von statischen Datenbeschreibungen zu tun. Das können Parserlisten, oder Sprachbeschreibungen, die baumartig vorliegen oder einfach nur simple Arrays sein.
In den meisten Fällen versuche ich solche Strukturen als konstante PODs (Plain Old Data) aufzubauen. D.h. also in einer Form:
- sodass kein weiterer Code notwendig ist um diese zu initialisieren (z.B. vermeinden von Konstruktoren/Destruktoren)
- dass die Daten sogar möglichst als const deklariert werden und so in einem Code Segment in Modulen geshared werden können.
- dass sie sich leicht aus einer „Art Datenbank“ in C/C++ Code erzeugen lassen.
Oft haben diese Datenstrukturen eine komplexe Struktur und auch einen Aufbau nach bestimmten Regeln, wie z.B. dass alle Daten eines Arrays sortiert sein müssen. Dadurch kann der Algorithmus, der diese Daten nutzt natürlich effektiver geschrieben werden, als wenn er auf einem unsortierten Array aufsetzt.
Eine Erfahrung, die ich heute weitergeben will, lautet nun:
Vertraue niemals darauf, dass die Daten in statischen konstanten Strukturen so vorliegen wie Du es glaubst… zumindest nicht in der Debug Version! 😉
Hintergrund für diese Erfahrung ist ein Bug, der sich genau aus einer Annahme einschlich, das statische Daten in einer bestimmten Regel vorliegen. In dem aktuellen Fall war es die bereits erwähnte Regel: „Der Array ist sortiert, nach einer ID“. Das Ganze war sogar am Anfang der besagten Struktur und im Code korrekt und ausführlich dokumentiert. Allerdings ist der Sourcecode dieser Tabelle mehrere 1000 Zeilen lang und ein Entwickler fügte nun einfach ein paar neue Einträge an das Ende der Liste an.
<ironiemode>Außerdem, wer liest schon interne Programmdokumentationen, wo wir alle (insb. der Autor und die geschätzten Leser dieses Artikels) doch fähig sind, den Sinn und Zweck von Code mit einem Blick zu erfassen?</ironiemode>
Die Folge war: „Nun ist der Array nicht mehr sortiert“ und die weitere Folge war, dass ein Algorithmus, der auf einer binären Suche basierte, nicht fand, was er finden sollte.
Dabei hätte Stück simpler Validierungscode, vermeiden können, dass dieser Code in Produktion ging:
class CSomeMightyDataHandler
{
public:
CSomeMightyDataHandler();
...
private:
// complex static data
struct S_COMPLEXDATA
{
...
};
static const S_COMPLEXDATA *m_pComplexData;
// Validator for Debug mode
#ifdef _DEBUG
static const bool m_bValidated;
static bool ValidateData();
#endif
...
};
#ifdef _DEBUG
// Validation for debug code only!
const bool CSomeMightyDataHandler::m_bValidated = CSomeMightyDataHandler::ValidateData();
bool CSomeMightyDataHandler::ValidateData()
{
// Do some validation and reset bResult on any failure
bool bResult = true;
...
ASSERT(bResult);
return bResult;
}
#endif
Mit solch einem Code wäre sofort ein ASSERT nach der ersten inkorrekten Code Ergänzung geflogen.
PS: Folgende Annahmen stimmen:
- Der Programmierer, der den Code falsch hinzugefügt war ich selbst.
- Der Programmierer, der beim Design der Klasse keinen Validierungscode geschrieben hat war auch ich selbst.
- Ich hatte wirklich vergessen, wie dieser Algorithmus ursprünglich mal von mir geplant war (sprich, dass eine Sortierung beachtet werden muss).
- Ich kann mir in den Hintern beißen für soviel selbst gemachte Dummheit.
1. Sowas passiert mir leider auch nur zu häufig, und ich bin auch schon ganz wundgebissen
2. „versuche“ ich mehr in richtung unit tests zu gehen das ich sowas schon beim compile erkenne, denn die erfahrung hat mich gelehrt, das es zwar schön ist ein debug assert zu haben, wenn man dann aber im debug nicht an der stelle vorbeikommt hat man schon wieder einen grund zum wilden um sich beisen 😉
und ja auch sowas ist bei mir schon vorgekommen 😉
Hast Du den static const bool Variable mit der entsprechenden Initialisierung im Code bemerkt?
Dadurch wird der Test-Code für diesen Part immer ausgeführt!
Genau deshalb habe ich das so gebaut.
Wer initialisiert denn m_pComplexData?
Ich meine, wie stellst Du sicher, dass ValidateData() immer ein initialisiertes m_pComplexData vorliegt?
Machst Du da evtl. Annahmen über die Initialisationsreihenfolge statischer Variablen?
Hmm…
m_pComplexData ist ein POD!
Insofern gibt es hier keine Intialisierung. Ich habe auch keine Konstruktoren. Defakto läuft hier kein Stück Code für die Initialisierung.
Ich brauche also auch keine Annahme hier machen 😉