better error reporting for static init ordering issues w.r.t config variables

This commit is contained in:
David Rose 2009-04-17 21:53:39 +00:00
parent 7852b4291d
commit 5a74ea30dd
7 changed files with 132 additions and 21 deletions

View File

@ -71,7 +71,7 @@ INLINE ConfigVariable::
////////////////////////////////////////////////////////////////////
INLINE const ConfigDeclaration *ConfigVariable::
get_default_value() const {
nassertr(_core != (ConfigVariableCore *)NULL, (ConfigDeclaration *)NULL);
nassertr(is_constructed(), (ConfigDeclaration *)NULL);
return _core->get_default_value();
}
@ -83,7 +83,7 @@ get_default_value() const {
////////////////////////////////////////////////////////////////////
INLINE const string &ConfigVariable::
get_string_value() const {
nassertr(_core != (ConfigVariableCore *)NULL, *new string());
nassertr(is_constructed(), *new string());
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_string_value();
}
@ -98,7 +98,7 @@ get_string_value() const {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
set_string_value(const string &string_value) {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->make_local_value()->set_string_value(string_value);
}
@ -111,7 +111,7 @@ set_string_value(const string &string_value) {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
clear_value() {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->clear_local_value();
}
@ -124,7 +124,7 @@ clear_value() {
////////////////////////////////////////////////////////////////////
INLINE int ConfigVariable::
get_num_words() const {
nassertr(_core != (ConfigVariableCore *)NULL, 0);
nassertr(is_constructed(), 0);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_num_words();
}
@ -139,7 +139,7 @@ get_num_words() const {
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
has_string_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, false);
nassertr(is_constructed(), false);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->has_string_word(n);
}
@ -152,7 +152,7 @@ has_string_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
has_bool_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, false);
nassertr(is_constructed(), false);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->has_bool_word(n);
}
@ -165,7 +165,7 @@ has_bool_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
has_int_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, false);
nassertr(is_constructed(), false);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->has_int_word(n);
}
@ -178,7 +178,7 @@ has_int_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
has_int64_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, false);
nassertr(is_constructed(), false);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->has_int64_word(n);
}
@ -191,7 +191,7 @@ has_int64_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
has_double_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, false);
nassertr(is_constructed(), false);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->has_double_word(n);
}
@ -205,7 +205,7 @@ has_double_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE string ConfigVariable::
get_string_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, string());
nassertr(is_constructed(), string());
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_string_word(n);
}
@ -219,7 +219,7 @@ get_string_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
get_bool_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, false);
nassertr(is_constructed(), false);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_bool_word(n);
}
@ -233,7 +233,7 @@ get_bool_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE int ConfigVariable::
get_int_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, 0);
nassertr(is_constructed(), 0);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_int_word(n);
}
@ -247,7 +247,7 @@ get_int_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE PN_int64 ConfigVariable::
get_int64_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, 0);
nassertr(is_constructed(), 0);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_int64_word(n);
}
@ -261,7 +261,7 @@ get_int64_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE double ConfigVariable::
get_double_word(int n) const {
nassertr(_core != (ConfigVariableCore *)NULL, 0.0);
nassertr(is_constructed(), 0.0);
const ConfigDeclaration *decl = _core->get_declaration(0);
return decl->get_double_word(n);
}
@ -274,7 +274,7 @@ get_double_word(int n) const {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
set_string_word(int n, const string &value) {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->make_local_value()->set_string_word(n, value);
}
@ -286,7 +286,7 @@ set_string_word(int n, const string &value) {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
set_bool_word(int n, bool value) {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->make_local_value()->set_bool_word(n, value);
}
@ -298,7 +298,7 @@ set_bool_word(int n, bool value) {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
set_int_word(int n, int value) {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->make_local_value()->set_int_word(n, value);
}
@ -310,7 +310,7 @@ set_int_word(int n, int value) {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
set_int64_word(int n, PN_int64 value) {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->make_local_value()->set_int_word(n, value);
}
@ -322,6 +322,26 @@ set_int64_word(int n, PN_int64 value) {
////////////////////////////////////////////////////////////////////
INLINE void ConfigVariable::
set_double_word(int n, double value) {
nassertv(_core != (ConfigVariableCore *)NULL);
nassertv(is_constructed());
_core->make_local_value()->set_double_word(n, value);
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariable::is_constructed
// Access: Protected
// Description: Returns true if the constructor has been called and
// _core initialized, false if the constructor has not
// yet been called and _core is NULL. This is intended
// to be placed in an assertion check, to guard against
// static-init ordering issues.
////////////////////////////////////////////////////////////////////
INLINE bool ConfigVariable::
is_constructed() const {
#ifndef NDEBUG
if (_core == (ConfigVariableCore *)NULL) {
report_unconstructed();
return false;
}
#endif
return true;
}

View File

@ -13,3 +13,20 @@
////////////////////////////////////////////////////////////////////
#include "configVariable.h"
#include "config_prc.h"
////////////////////////////////////////////////////////////////////
// Function: ConfigVariable::report_unconstructed
// Access: Protected
// Description: Displays a suitable error message when an
// unconstructed ConfigVariable is attempted to be used.
// This normally indicates a static-init ordering issue.
////////////////////////////////////////////////////////////////////
void ConfigVariable::
report_unconstructed() const {
prc_cat->error()
<< "ConfigVariable " << this
<< " accessed before its constructor has run!\n";
record_unconstructed();
}

View File

@ -67,6 +67,10 @@ PUBLISHED:
INLINE void set_int_word(int n, int value);
INLINE void set_int64_word(int n, PN_int64 value);
INLINE void set_double_word(int n, double value);
protected:
INLINE bool is_constructed() const;
void report_unconstructed() const;
};
#include "configVariable.I"

View File

@ -14,6 +14,8 @@
#include "configVariableBase.h"
ConfigVariableBase::Unconstructed *ConfigVariableBase::_unconstructed;
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableBase::Constructor
// Access: Protected
@ -26,6 +28,13 @@ ConfigVariableBase(const string &name,
const string &description, int flags) :
_core(ConfigVariableManager::get_global_ptr()->make_variable(name))
{
#ifndef NDEBUG
if (was_unconstructed()) {
prc_cat->error()
<< "Late constructing " << this << ": " << name << "\n";
}
#endif // NDEBUG
if (value_type != VT_undefined) {
_core->set_value_type(value_type);
}
@ -38,3 +47,43 @@ ConfigVariableBase(const string &name,
_core->set_flags(flags);
}
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableBase::record_unconstructed
// Access: Protected
// Description: Records that this config variable was referenced
// before it was constructed (presumably a static-init
// ordering issue). This is used to print a useful
// error message later, when the constructor is actually
// called (and we then know what the name of the
// variable is).
////////////////////////////////////////////////////////////////////
void ConfigVariableBase::
record_unconstructed() const {
#ifndef NDEBUG
if (_unconstructed == (Unconstructed *)NULL) {
_unconstructed = new Unconstructed;
}
_unconstructed->insert(this);
#endif
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableBase::was_unconstructed
// Access: Protected
// Description: Returns true if record_unconstructed() was ever
// called on this pointer, false otherwise.
////////////////////////////////////////////////////////////////////
bool ConfigVariableBase::
was_unconstructed() const {
#ifndef NDEBUG
if (_unconstructed != (Unconstructed *)NULL) {
Unconstructed::const_iterator ui = _unconstructed->find(this);
if (ui != _unconstructed->end()) {
return true;
}
}
#endif
return false;
}

View File

@ -21,6 +21,7 @@
#include "configDeclaration.h"
#include "configVariableManager.h"
#include "vector_string.h"
#include "pset.h"
// Use this macro to wrap around a description passed to a
// ConfigVariable constructor. This allows the description to be
@ -69,7 +70,13 @@ PUBLISHED:
INLINE void write(ostream &out) const;
protected:
void record_unconstructed() const;
bool was_unconstructed() const;
ConfigVariableCore *_core;
typedef pset<const ConfigVariableBase *> Unconstructed;
static Unconstructed *_unconstructed;
};
INLINE ostream &operator << (ostream &out, const ConfigVariableBase &variable);

View File

@ -181,7 +181,18 @@ set_description(const string &description) {
}
return;
}
if (description.empty()) {
// If the new description is empty, we don't do anything.
return;
}
if (_description.empty()) {
// If the previous description was empty, we quietly replace it.
_description = description;
return;
}
prc_cat->warning()
<< "changing description for ConfigVariable "
<< get_name() << ".\n";

View File

@ -437,6 +437,9 @@ assert_failure(const char *expression, int line,
nout << "Assertion failed: " << message << "\n";
// This is redefined here, shadowing the defining in config_prc.h,
// so we can guarantee it has already been constructed.
ConfigVariableBool assert_abort("assert-abort", false);
if (assert_abort) {
#ifdef WIN32
// How to trigger an exception in VC++ that offers to take us into