Boost C++ Libraries: Ticket #3265: parse vectors https://svn.boost.org/trac10/ticket/3265 <p> As discussed on the boost users mailinglist, when a user specified a vector as return target in their options_description, that is: </p> <p> <code>("opt", po::value&lt;float&gt;&amp;fA), "")</code> </p> <p> instead of </p> <p> <code>("vecopt", po::value&lt;vector&lt;float&gt;&gt;&amp;vfa), "")</code> </p> <p> a simple syntax for specifying such a vector (that is, an option that can occur multiple times), on the command line, in a config file or even in an environment variable (sic!) can be used. </p> <p> Compare (for command line) [--test "(1 2 3)"] with [--test 1 --test 2 --test 3]. </p> <p> Adding support for this syntax in the validate() function for vectors ensures that the braces (feel free to change them to another symbol that makes better sense to you) will only be processed specially when the user specified a vector as output variable. It will not break any existing option input systems defined by users. </p> <p> Note that it will still be possible to specify options multiple times, the values of which will be accumulated in the vector. One could even supply multiple vector-syntax inputs for the same option, which would then get concatenated together. </p> <p> The code is not yet totally finished, I need to add support for handling vectors of strings, marked as TODO in the code. </p> <p> proposed diff: </p> <pre class="wiki">Index: value_semantic.hpp =================================================================== --- value_semantic.hpp (revision 54915) +++ value_semantic.hpp (working copy) @@ -8,6 +8,8 @@ #include &lt;boost/throw_exception.hpp&gt; +#include &lt;boost/algorithm/string.hpp&gt; + namespace boost { namespace program_options { extern BOOST_PROGRAM_OPTIONS_DECL std::string arg; @@ -124,7 +126,8 @@ #endif /** Validates sequences. Allows multiple values per option occurrence - and multiple occurrences. */ + and multiple occurrences. This function is only called when user + supplied vector&lt;T&gt; as datatype in option_description */ template&lt;class T, class charT&gt; void validate(boost::any&amp; v, const std::vector&lt;std::basic_string&lt;charT&gt; &gt;&amp; s, @@ -142,11 +145,37 @@ /* We call validate so that if user provided a validator for class T, we use it even when parsing vector&lt;T&gt;. */ - boost::any a; - std::vector&lt;std::basic_string&lt;charT&gt; &gt; v; - v.push_back(s[i]); - validate(a, v, (T*)0, 0); - tv-&gt;push_back(boost::any_cast&lt;T&gt;(a)); + + vector&lt;std::basic_string&lt;charT&gt;&gt; value; + + // test if vector notation is used in input + if (*s[i].begin() == '(' &amp;&amp; *s[i].rbegin() == ')') + { + // test if it is a vector of strings + if (is_same&lt;T, string&gt;::value||is_same&lt;T, wstring&gt;::value) + { + /** TODO: needs special treatment, cant simply split + on space character + For now, proceed as before */ + value.push_back(s[i]); + } + else + { + split( value, s[i].substr(1, s[i].size()-2), is_any_of( " ") ); + } + } + else + value.push_back(s[i]); + + // validate option values + for (unsigned j = 0; j &lt; value.size(); j++) + { + boost::any a; + std::vector&lt;std::basic_string&lt;charT&gt; &gt; v; + v.push_back(value[j]); + validate(a, v, (T*)0, 0); + tv-&gt;push_back(boost::any_cast&lt;T&gt;(a)); + } } catch(const bad_lexical_cast&amp; /*e*/) { boost::throw_exception(invalid_option_value(s[i])); </pre> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/3265 Trac 1.4.3 Diederick C. Niehorster <dcnieho@…> Thu, 23 Jul 2009 06:19:04 GMT cc set https://svn.boost.org/trac10/ticket/3265#comment:1 https://svn.boost.org/trac10/ticket/3265#comment:1 <ul> <li><strong>cc</strong> <span class="trac-author">dcnieho@…</span> added </li> </ul> <p> Full proposed patch, now including support for vectors of strings with proper double quote support. </p> <pre class="wiki">Index: value_semantic.hpp =================================================================== --- value_semantic.hpp (revision 54915) +++ value_semantic.hpp (working copy) @@ -8,6 +8,8 @@ #include &lt;boost/throw_exception.hpp&gt; +#include &lt;boost/algorithm/string.hpp&gt; + namespace boost { namespace program_options { extern BOOST_PROGRAM_OPTIONS_DECL std::string arg; @@ -124,7 +126,8 @@ #endif /** Validates sequences. Allows multiple values per option occurrence - and multiple occurrences. */ + and multiple occurrences. This function is only called when user + supplied vector&lt;T&gt; as datatype in option_description */ template&lt;class T, class charT&gt; void validate(boost::any&amp; v, const std::vector&lt;std::basic_string&lt;charT&gt; &gt;&amp; s, @@ -142,11 +145,41 @@ /* We call validate so that if user provided a validator for class T, we use it even when parsing vector&lt;T&gt;. */ - boost::any a; - std::vector&lt;std::basic_string&lt;charT&gt; &gt; v; - v.push_back(s[i]); - validate(a, v, (T*)0, 0); - tv-&gt;push_back(boost::any_cast&lt;T&gt;(a)); + + vector&lt;std::basic_string&lt;charT&gt;&gt; value; + + // test if vector notation is used in input + if (*s[i].begin() == '(' &amp;&amp; *s[i].rbegin() == ')') + { + // test if a vector of strings is requested + if (is_same&lt;T, string&gt;::value||is_same&lt;T, wstring&gt;::value) + { + /** Strings needs special treatment, cant simply split + on space character as that might be part of the + string. Using split_winmain we can allow for the + same grammar within the brackets () as for strings + on the command line, that is, e.g., use "" to + encapsulate strings with spaces. For complete + grammar, see links in the split_winmain source. */ + value = split_winmain(s[i].substr(1, s[i].size()-2)); + } + else + { + split( value, s[i].substr(1, s[i].size()-2), is_any_of( " ") ); + } + } + else + value.push_back(s[i]); + + // validate option values + for (unsigned j = 0; j &lt; value.size(); j++) + { + boost::any a; + std::vector&lt;std::basic_string&lt;charT&gt; &gt; v; + v.push_back(value[j]); + validate(a, v, (T*)0, 0); + tv-&gt;push_back(boost::any_cast&lt;T&gt;(a)); + } } catch(const bad_lexical_cast&amp; /*e*/) { boost::throw_exception(invalid_option_value(s[i]));}}} </pre> Ticket Sascha Ochsenknecht Tue, 08 Dec 2009 09:52:38 GMT <link>https://svn.boost.org/trac10/ticket/3265#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3265#comment:2</guid> <description> <p> Here is the archived discussion on boost users list: <a class="ext-link" href="http://lists.boost.org/boost-users/2009/07/49758.php"><span class="icon">​</span>boost user</a> </p> </description> <category>Ticket</category> </item> <item> <author>Diederick C. Niehorster <dcnieho@…></author> <pubDate>Fri, 17 Jun 2011 05:26:24 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/3265#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3265#comment:3</guid> <description> <p> Dear Volodya and/or Sascha, </p> <p> I still have interest in this addition and am using code made possible by it daily, so far I haven't run into any problems. Could you consider looking at this and including it? </p> <p> Thank you very much for your time, let me know if I can help in any way! </p> <p> Best, Dee </p> </description> <category>Ticket</category> </item> <item> <author>Diederick C. Niehorster <dcnieho@…></author> <pubDate>Thu, 27 Dec 2012 10:21:09 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/3265#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3265#comment:4</guid> <description> <p> Hmm, here is a slightly updated version of the patch. Some usage of std wasn't prefixed with std:: and the call to split should split on any whitespace (tabs are fine too after all) and have token compressing on in case e.g. multiple spaces delimit two entries. </p> <pre class="wiki">Index: value_semantic.hpp =================================================================== --- value_semantic.hpp (revision 54915) +++ value_semantic.hpp (working copy) @@ -8,6 +8,8 @@ #include &lt;boost/throw_exception.hpp&gt; +#include &lt;boost/algorithm/string.hpp&gt; + namespace boost { namespace program_options { extern BOOST_PROGRAM_OPTIONS_DECL std::string arg; @@ -124,7 +126,8 @@ #endif /** Validates sequences. Allows multiple values per option occurrence - and multiple occurrences. */ + and multiple occurrences. This function is only called when user + supplied vector&lt;T&gt; as datatype in option_description */ template&lt;class T, class charT&gt; void validate(boost::any&amp; v, const std::vector&lt;std::basic_string&lt;charT&gt; &gt;&amp; s, @@ -142,11 +145,41 @@ /* We call validate so that if user provided a validator for class T, we use it even when parsing vector&lt;T&gt;. */ - boost::any a; - std::vector&lt;std::basic_string&lt;charT&gt; &gt; v; - v.push_back(s[i]); - validate(a, v, (T*)0, 0); - tv-&gt;push_back(boost::any_cast&lt;T&gt;(a)); + + std::vector&lt;std::basic_string&lt;charT&gt;&gt; value; + + // test if vector notation is used in input + if (*s[i].begin() == '(' &amp;&amp; *s[i].rbegin() == ')') + { + // test if a vector of strings is requested + if (is_same&lt;T, std::string&gt;::value||is_same&lt;T, std::wstring&gt;::value) + { + /** Strings needs special treatment, cant simply split + on space character as that might be part of the + string. Using split_winmain we can allow for the + same grammar within the brackets () as for strings + on the command line, that is, e.g., use "" to + encapsulate strings with spaces. For complete + grammar, see links in the split_winmain source. */ + value = split_winmain(s[i].substr(1, s[i].size()-2)); + } + else + { + split( value, s[i].substr(1, s[i].size()-2), is_space(), token_compress_on ); + } + } + else + value.push_back(s[i]); + + // validate option values + for (unsigned j = 0; j &lt; value.size(); j++) + { + boost::any a; + std::vector&lt;std::basic_string&lt;charT&gt; &gt; v; + v.push_back(value[j]); + validate(a, v, (T*)0, 0); + tv-&gt;push_back(boost::any_cast&lt;T&gt;(a)); + } } catch(const bad_lexical_cast&amp; /*e*/) { boost::throw_exception(invalid_option_value(s[i]));}}} </pre><p> Best and thanks, Dee </p> </description> <category>Ticket</category> </item> </channel> </rss>