Console: More flexible default_value handling
g0dil [Fri, 11 Apr 2008 23:50:04 +0000 (23:50 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@807 270642c3-0616-0410-b53a-bc976706d245

Console/Mainpage.dox
Console/ParsedCommand.mpp

index 3e8e458..7323fbf 100644 (file)
     From this information the command callback gets a list of arguments or tokens which then can be
     interpreted in an arbitrary way.
     \code
-    void test1(std::ostream & os, senf::console::ParseCommandInfo const & command)
+    void fun1(std::ostream & os, senf::console::ParseCommandInfo const & command)
     {
         // We take exactly one argument
         if (command.arguments().size() != 1) 
     'doc()':
     \code
     senf::console::root()
-        .add("test1", &test1)
+        .add("test1", &fun1)
         .doc("Usage:\n"
              "    test1 arg\n"
              "\n"
     feature allows to register (almost) arbitrary callbacks.
 
     \code
-    std::string test2(std::string const & arg)
+    std::string fun(std::string const & arg)
     {
         return arg;
     }
     senf::console::DirectoryNode.
     \code
     senf::console::root()
-        .add("test2", &test2);
+        .add("test2", &fun2);
     \endcode
     The functionality is now identical to \c test1:
     \htmlonly
     raised.
 
     \code
-    void test3(std::ostream & os, unsigned n, std::string text)
+    void fun3(std::ostream & os, unsigned n, std::string text)
     {
         // It's perfectly valid to check additional constraints here and throw a
         // SyntaxErrorException. In this case, the next overload will be tried. However, you must
     using namespace senf::console::kw;
     
     senf::console::root()
-        .add("test3", &test3)
+        .add("test3", &fun3)
         .doc("Echo text to the console")
         .overloadDoc("Repeat 'text' for 'n' lines")
         .arg( name = "n", description = "Number of repetitions" )
         .arg( name = "text", description = "Message to output" );
     senf::console::root()
-        .add("test3", &test2)
+        .add("test3", &fun2)
         .overloadDoc("Echo the 'text' argument")
-        .arg( name = "text", description = "Message to output" );
+        .arg( name = "text" );
     \endcode
 
-    We can now call \c test2 with one or two arguments:
+    We can now call \c test3 with one or two arguments:
 
     \htmlonly
     <pre>
     using namespace senf::console::kw;
 
     senf::console::root()
-        .add("test4", &test2b)
-        .arg()
-        .arg( default_value = "ok" );
+        .add("test4", &fun3)
+        .arg( name = "n", description = "Number of repetitions", default_value = 1 )
+        .arg( name = "text", description = "Message to output" );
     \endcode
-    (Default values must not necessarily be declared in the callback function too.) Of course,
-    default values can be used together with overloading. 
 
-    There must be no argument without default value after an argument with a default value
-    declared. This fill fail at compile time. 
+    (Default values must not necessarily be declared in the callback function too.) Of course,
+    default values can be used together with overloading. Default (optional) value support is quite
+    flexible, it is not mandatory, for default values to be specified only for the trailing
+    arguments.
+    
+    \htmlonly
+    <pre>
+    server:/$ test4 echo
+    echo
+    server:/$ test4 4 ok
+    ok
+    ok
+    ok
+    ok
+    server:/$ help test4
+    Usage:
+        test4 n:unsigned [text:string]
+    
+    With:
+        n         Number of repetitions
+        text      Message to output
+            default value: ok
+    server:/$
+    </pre>
+    \endhtmlonly
 
     \subsection console_auto_summary Attribute summary
 
index 0c0c55c..3d998c4 100644 (file)
@@ -33,6 +33,7 @@
 #include <boost/preprocessor/repetition/enum_binary_params.hpp>
 #include <boost/preprocessor/cat.hpp>
 #include <boost/preprocessor/arithmetic/inc.hpp>
+#include <boost/preprocessor/arithmetic/sub.hpp>
 #include <boost/preprocessor/repetition/repeat.hpp>
 #include <boost/type_traits/remove_reference.hpp>
 #include <boost/type_traits/remove_const.hpp>
@@ -124,20 +125,25 @@ prefix_ void senf::console::ParsedCommandOverload<FunctionTraits, BOOST_PP_ITERA
 v_execute(std::ostream & os, ParseCommandInfo const & command)
     const
 {
-    if ( command.arguments().size() > BOOST_PP_ITERATION()
-         || (command.arguments().size() < BOOST_PP_ITERATION()
-             && ! arg( command.arguments().size() ).hasDefault) )
+    if ( command.arguments().size() > BOOST_PP_ITERATION() )
         throw SyntaxErrorException("invalid number of arguments");
+    int nDefaults ( BOOST_PP_ITERATION() - command.arguments().size() );
 
-    ParseCommandInfo::argument_iterator i (command.arguments().begin());
-    ParseCommandInfo::argument_iterator const i_end (command.arguments().end());
+    typedef typename boost::range_const_reverse_iterator<ParseCommandInfo::ArgumentsRange>::type
+        riterator;
+    riterator i (boost::rbegin(command.arguments()));
+    riterator const i_end (boost::rend(command.arguments()));
 
 #   define mpp_l(z,n,d)                                                                           \
+        if (i == i_end)                                                                           \
+            throw SyntaxErrorException("invalid number of arguments");                            \
         mpp_ArgTypeN(n) mpp_ArgN(n) (arg< mpp_ArgTypeN(n) >( n ).defaultValue);                   \
-        if (i != i_end)                                                                           \
+        if (! arg(n).hasDefault || nDefaults-- <= 0)                                              \
             ArgumentTraits< mpp_ArgTypeN(n) >::parse( *(i++), mpp_ArgN(n) );
-    BOOST_PP_REPEAT( BOOST_PP_ITERATION(), mpp_l, _ ) 
+#   define mpp_l_(z,n,d) mpp_l(z, BOOST_PP_SUB(BOOST_PP_DEC(BOOST_PP_ITERATION()), n), d)
+    BOOST_PP_REPEAT( BOOST_PP_ITERATION(), mpp_l_, _ ) 
 #   undef mpp_l
+#   undef mpp_l_
 
     detail::CheckVoidReturn<typename traits::result_type>::call(
         boost::bind(function_, boost::ref(os)