
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi, [I hope I'm on the right list..] 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. In my vision, things would work like this: po::options_description generic ("generic options"); generic.add_options () // e.g. --help, --version ... po::options_description sub1 ("options for module 1"); ... po::options_description sub2 ("options for module 2"); ... po::options_description preferences_options; preferences_options.add (sub1). add (sub2); ... // do normal parsing (command line, config file, ...) ... preferences_dialog pref (preferences_options); ... An open question is, when to propagate the options. My program works in some sort of main-loop, at the beginning of which I do pref.commit (vm); // save options, like store would do, but overwrite // and remember all options that changed if (pref.changed ("foo-option")) take care of this // e.g. reload something ... The preferences dialog should have one tab per option group (that is, one for "options for module 1" and one for "options for module 2"). Every of that tabs should display widgets to manipulate the respective options, that is, for every option, it's name, help information, some sort of input widget, maybe a reset button if there's a default value. Trying to implement all this, I encountered several problems. I'll try to make a list of things that have to be possible, and discuss all items in turn. 1 change the values of options in a variables_map 2 find out the type of an option 3 convert from an option value (stored in a Boost.Any) to some screen representation 4 find out what options in a po::options_description are members of what option groups 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. 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. 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 ;). 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). 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. All in all, I think the use of private (friend) interfaces overly restricts the reusability of Boost.Program_options. I don't know if what I try is in the scope of what should be possible, but I'm happy to discuss the problems I reported, both from the perspective of changing the library and from the perspective of changing my demands. 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> > > () (where I overloaded validate for checked_val's to apply the functor encoded in the type). - -- - -ness- -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFF/BTYvD/ijq9JWhsRAsA4AKCBbigeh/fa3k1jEBKTmJ01JPz1FwCfQE7J 3CQIyp/rRMQb4VFnQqHR3Kg= =FRtW -----END PGP SIGNATURE-----

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

On 3/20/07, Vladimir Prus <ghost@cs.msu.su> wrote:
Tom Bachmann wrote:
<snip>
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.
Add me to the list of people trying to make boost::program_options do a little more than it was originally designed for ;) Just been over this exact problem today. Disallowing assignments that change the value type sounds good to me. You'll probably catch it the way things currently are because variables_map["option"].as<double>() will throw boost::bad_any_cast later on when you try to acess the option again, but an exception on wrongful assignment would be preferable. Regards, -- Tarjei
participants (3)
-
Tarjei Knapstad
-
Tom Bachmann
-
Vladimir Prus