c++gflags

Usage of FLAGS_nono##name in gflags source code


I'm reading gflags's source code recently. And the comment here really confuses me.

It's saying FLAGS_nono##name is introduced to ensure static initialization. But as far as I know, if you define a global var like: int x = 20;

x is still statically initialized. The what's the necessity of FLAGS_nono##name here.

Do I misunderstand it?

Thanks.

// 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

For more context, see https://code.google.com/p/gflags/source/browse/src/gflags.h.in#471

Another question is the FlagRegisterer class used in the codes mentioned above. I'm wondering what's the necessity of such class and a global var here, instead of simply defining a function.

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
}

Solution

  • You're right that something like int i = 10; performs static initialisation. It would therefore be possible to write

    #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
    

    However, please beware that this is a macro. Multiple references to value cause multiple expansions. If value is not a constant, if it contains e.g. a function call, this would cause the function to be called twice.

    That could be avoided by copying one variable to the other:

    #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
    

    But now you've got dynamic initialisation, where you'd want static initialisation.

    To make sure both work properly, you need an additional helper variable, and that's what the code you're asking about does.