
Tom Bachmann wrote: Hi Tom,
[I hope I'm on the right list..]
You are!
I have been trying to extend Boost.Program_options, and I found this very hard. I'd like to let you know of the problems I'm experiancing.
Basically, I am writing a relatively long-running program, with lots of options. I want to be able to change these options at run-time, for doing so, I want to create a "preferences" window or such, and (of course) I want to reuse the options description I already created to parse the command line and config file options.
This is an interesting direction!
1 change the values of options in a variables_map
The only way to do this (as far as I can see) is the private interface that the store function uses. I currently do
const_cast<boost::program_options::variable_value&> (vm[opt -> long_name ()]).value () = val;
which is neither clean nor complete (the internal reference to value_semantic is not updated, but this should not be necessary anyway, I think). The easiest fix would be to add a public interface to change the value of an option in a variables map, by making op[] return a non-const reference. I'm not sure if code should be added to call semantic -> notify automically.
Yep, I guess we need non-const-returning[] for sure. We probably should have variable_value overload operator=, and call semantic->notify. Do you think disallowing assignements that change value type is OK? Say, if you try to assign int to value of type double, you get exception.
2 find out the type of an option
This is not strictly necessary, but I'd be nice to e.g show a checkbox for bool options, a spin button for numeric options, and an entry field for other options. I currently infer the type by querying the Boost.Any that holds the option, potentially after applying the default value. This is not foolproof, however, because it fails for options that have no default value and that have not been set before.
Technically speaking, the value_semantic base class does not require an option to have any type at all. At the same time, in 100% of known cases the type for an option is known. Perhaps I can either add 'type' method to value_semantics that returns type_info* and may return NULL, or just make it return type_info& and demand that all options have a fixed type. Will either of those approaches work for you?
3 convert from an option value to some screen representation
This is not strictly necessary either, but it'd be nice to show the user the current value of an option ;).
Heh!
This is some sort of "backward parse" and can be easily put into the value_semantic class. Afterall, this is already done, when converting the default value into a string. It is, however, not strictly something one would usually expect from the value_semantic class. Currently I take the type information I already have (see 2), cast to the respective type and then convert using Boost.Lexical_cast. This is quiet cumbersome, because I need to know every possible type in advance and (automatically) instantiate code for every such type and dynamically dispatch to the right code (this is the main reason I created the dispatcher mentioned in another mail of mine).
One another approach is make variable_value record original tokens it was constructed from. This correlates with your point (1) above -- perhaphs after UI was changed, value_semantics->parse should be called to get new boost::any and the parsed tokens should be stored in variable_value.
4 find out what options in a po::options_description are members of what option groups
This is necessary to group the options in multiple tabs. Again, there are only private interfaces to access the information.
The problem here is that when you add one options_description to another options description, the original object is not retained, so it's not simple to have "parent pointer". It might be possible to have "parent group name" field in an option. Would that be too cumbersome to construct UI hierarchy grouping options by string parent name?
And I have a (unrelated) suggestion for an improvement: add an auxiliary functor for validating. Often there are certain conditions imposed on the options, e.g. a numeric option has to be in some interval or such. This checking can be done once after parsing, but it'd be cleaner to directly incorporate it. One could encode all the information in the type, but that'd be ugly. Using Boost.Lambda, I could imagine a syntax like:
po::checked_value<int> (_1 > 0 && _1 < 5)
instead of
po::value<checked_val<int, bounds_checker<int, 1, 4> > > ()
Sigh. This is on todo list, but I haven't had any time for that. - Volodya