Использование имени FLAGS_nono## в исходном коде gflags
Я недавно читаю исходный код gflags. И комментарий здесь действительно смущает меня.
В нем говорится, что имя FLAGS_nono## введено для обеспечения статической инициализации. Но, насколько я знаю, если вы определите глобальную переменную вроде: int x = 20;
х все еще статически инициализирован. Что за имя FLAGS_nono## здесь.
Я неправильно понимаю?
Благодарю.
// Each command-line flag has two variables associated with it: one
// with the current value, and one with the default value. However,
// we have a third variable, which is where value is assigned; it's a
// constant. This guarantees that FLAG_##value is initialized at
// static initialization time (e.g. before program-start) rather than
// than global construction time (which is after program-start but
// before main), at least when 'value' is a compile-time constant. We
// use a small trick for the "default value" variable, and call it
// FLAGS_no<name>. This serves the second purpose of assuring a
// compile error if someone tries to define a flag named no<name>
// which is illegal (--foo and --nofoo both affect the "foo" flag).
#define DEFINE_VARIABLE(type, shorttype, name, value, help) \
namespace fL##shorttype { \
static const type FLAGS_nono##name = value; \
/* We always want to export defined variables, dll or no */ \
GFLAGS_DLL_DEFINE_FLAG type FLAGS_##name = FLAGS_nono##name; \
type FLAGS_no##name = FLAGS_nono##name; \
static @GFLAGS_NAMESPACE@::FlagRegisterer o_##name( \
#name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \
&FLAGS_##name, &FLAGS_no##name); \
} \
using fL##shorttype::FLAGS_##name
Для получения дополнительной информации см. https://code.google.com/p/gflags/source/browse/src/gflags.h.in#471
Другой вопрос - класс FlagRegisterer, используемый в кодах, упомянутых выше. Мне интересно, зачем нужен такой класс и глобальная переменная здесь, вместо простого определения функции.
class GFLAGS_DLL_DECL FlagRegisterer {
public:
FlagRegisterer(const char* name, const char* type,
const char* help, const char* filename,
void* current_storage, void* defvalue_storage);
};
// --------------------------------------------------------------------
// FlagRegisterer
// This class exists merely to have a global constructor (the
// kind that runs before main(), that goes an initializes each
// flag that's been declared. Note that it's very important we
// don't have a destructor that deletes flag_, because that would
// cause us to delete current_storage/defvalue_storage as well,
// which can cause a crash if anything tries to access the flag
// values in a global destructor.
// --------------------------------------------------------------------
FlagRegisterer::FlagRegisterer(const char* name, const char* type,
const char* help, const char* filename,
void* current_storage, void* defvalue_storage) {
if (help == NULL)
help = "";
// FlagValue expects the type-name to not include any namespace
// components, so we get rid of those, if any.
if (strchr(type, ':'))
type = strrchr(type, ':') + 1;
FlagValue* current = new FlagValue(current_storage, type, false);
FlagValue* defvalue = new FlagValue(defvalue_storage, type, false);
// Importantly, flag_ will never be deleted, so storage is always good.
CommandLineFlag* flag = new CommandLineFlag(name, help, filename,
current, defvalue);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag); // default registry
}
1 ответ
Ты прав что то типа int i = 10;
выполняет статическую инициализацию. Поэтому можно было бы написать
#define DEFINE_VARIABLE(type, shorttype, name, value, help) \
namespace fL##shorttype { \
/* We always want to export defined variables, dll or no */ \
GFLAGS_DLL_DEFINE_FLAG type FLAGS_##name = value; \
type FLAGS_no##name = value; \
static @GFLAGS_NAMESPACE@::FlagRegisterer o_##name( \
#name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \
&FLAGS_##name, &FLAGS_no##name); \
} \
using fL##shorttype::FLAGS_##name
Однако, пожалуйста, будьте осторожны, что это макрос. Несколько ссылок на value
вызвать множественные расширения. Если value
не является константой, если она содержит, например, вызов функции, это приведет к тому, что функция будет вызвана дважды.
Этого можно избежать, скопировав одну переменную в другую:
#define DEFINE_VARIABLE(type, shorttype, name, value, help) \
namespace fL##shorttype { \
/* We always want to export defined variables, dll or no */ \
GFLAGS_DLL_DEFINE_FLAG type FLAGS_##name = value; \
type FLAGS_no##name = FLAGS_##name; \
static @GFLAGS_NAMESPACE@::FlagRegisterer o_##name( \
#name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \
&FLAGS_##name, &FLAGS_no##name); \
} \
using fL##shorttype::FLAGS_##name
Но теперь у вас есть динамическая инициализация, где вы хотите статическую инициализацию.
Чтобы убедиться, что оба работают правильно, вам нужна дополнительная вспомогательная переменная, и именно это делает код, о котором вы спрашиваете.