GNOME Bugzilla – Bug 744854
OptionContext parse bug
Last modified: 2015-02-25 09:43:36 UTC
After some fooling around with glibmm's command-line option handling, I noticed that I got different results compared to (what I believe is) an analogue glib situation. Basically I added a boolean type option 'feature' with two entries in the main group: '--enable-feature' and '--disable-feature'. Obviously depending on the initial value of the boolean variable, one of these options will correspond to the default behaviour. This works fine in glib, but fails in glibmm. Interestingly, if I remove the default option, it works as expected. I attached two files, one in C and one in C++ to demonstrate this problem. I am using glib 2.42.1 and glibmm 2.40.0.
Created attachment 297392 [details] C example: works as expected
Created attachment 297393 [details] C++ example: not working as expected...
Glib's g_option_context_parse() has access to pointers to the data in the application program, in your C example gboolean feature1 and gboolean feature2. It's no problem that two GOptionEntry elements contain pointers to the same variable. Glibmm's Glib::OptionGroup::add_entry() introduces an extra level of indirection. Each call to add_entry() with a bool argument allocates a new gboolean. A pointer to that gboolean is what is available to g_option_context_parse() when it's called from Glib::OptionContext::parse(). When the program is called with a --disable-feature1 argument, only one of the gbooleans corresponding to feature1 is set to false. Then both of them are copied to feature1, and the result depends of which one is copied last. There's another situation where this extra level of indirection gives surprising results. If the default value is set after the call to add_entry(), that assignment has no effect. Try bool feature2 = false; ................ //option_group.add_entry(entry1a, feature1); option_group.add_entry(entry1b, feature1); option_group.add_entry(entry2a, feature2); //option_group.add_entry(entry2b, feature2); feature2 = true; // Added and call the program with no arguments. feature2 == false! At the moment I don't know how to fix it. We do need the extra level of indirection in order to convert between C types and C++ types.
Thanks for looking into this. I have managed to circumvent this undesired behavior by removing the default options. Would it be possible to fix this by not using the allocated gboolean in the G_OPTION_ARG_NONE case and just continue working with the initial bool instead? Or would it be too messy to remove the extra indirection in this particular case?
It would be risky to let glib write directly to a bool variable, when it believes it's writing to a gboolean variable. Can we be sure a bool variable is identical to a gboolean? Regardless of which compiler has been used? I've fixed the issue in another way. I found a reasonably easy way to allocate just one common gboolean variable for several options that refer to the same bool variable. I even made it work that way for options of other types as well: If several options refer to the same C++ variable, they will all share the same C variable when g_option_context_parse() is called. The only restriction is that the test for same C++ variable only checks within one OptionGroup. It won't work if two options in different option groups refer to the same C++ variable. https://git.gnome.org/browse/glibmm/commit/?id=76c68945df98788984d1187c80a6f334391d090e
You're right: though gboolean is typedef'ed to int (through gint), the size of bool is left up entirely by the C++ implementation. Many thanks for fixing the bug with such an elegant solution!