C++ string constants and static variable initialization (or just use macros?)
I am writing a library and wanted to be as C++ centric as possible, remembering the old adage "Macros are evil".
In a source file, I had the following definitions:
const std::string DATA_DIR_ENV_STR = "DATADIR"
const std::string DFLT_DATA_DIR = "../data"
/*
#define DATA_DIR_ENV_STR "DATADIR"
#define DFLT_DATA_DIR "../data"
*/
std::string getRootDirectory()
{
char * datastr_ = getenv(DATA_DIR_ENV_STR);
if (datastr_)
return std::string(datastr_);
return DFLT_DATA_DIR;
}
// Header file
std::string getRootDirectory();
I then had a singleton class that was initialized like this:
bool mySingleton::inited = mySingleton::initialize();
bool mySingleton::initialize(){
std::string rootdir =开发者_如何转开发 getRootDirectory(); // <-SEGV when using const std::string
}
The library compiled fine, but when I linked an application to it, the app always SEGV'd. I used gdb to track down the problem and to my shock/horror, the string variables DATA_DIR_ENV_STR and DFLT_DATA_DIR had not yet been initialized when they were been accessed during the static variable initialization.
In the end I simply used macros to get around the issue. BUT, I can't help wondering, is this a variation of the 'static variable initialization fiasco'?. Is there another way to resolve this without using macros?
Yes, this is the static initialization fiasco biting your behind.
A way to avoid it is the "construct on first use" idiom (brain-compiled code):
// In the header
class mySingleton {
private:
static mySingleton *s_instance;
mySingleton();
public:
mySingleton &instance() {
if (!s_instance)
s_instance = new mySingleton();
return *s_instance;
}
};
// And in the source file...
mySingleton *mySingleton::s_instance;
However, in the case of string constants, you can also get away with simple char
pointers:
static char const *const DATA_DIR_ENV_STR = "DATADIR";
static char const *const DFLT_DATA_DIR = "../data";
It is a static initialisation issue, as you suspect.
I assume you use .c_str() in your getenv clause.
You could use const char * const rather than std::string in your statics.
namespace {
/*static*/ const char * const DATA_DIR_ENV_STR = "DATADIR";
/*static*/ const char * const DFLT_DATA_DIR = "../data";
}
Note use of anonymous namespace is generally preferred now to static.
Your other option would be a function that returned them thus:
namespace {
const std::string & DATA_DIR_ENV_STR()
{
static std::string s("DATADIR");
return s;
}
const std::string& DFLT_DATA_DIR()
{
static std::string s("../data");
return s;
}
}
It is indeed a manifestation of that. See http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14 for more information (and below for solutions).
EDIT: Note that if you can redesign so that you don't need a singleton that will automatically fix the problem.
精彩评论