Console; Support non-function-pointer parsed commands via boost::function
g0dil [Wed, 16 Apr 2008 08:57:06 +0000 (08:57 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@814 270642c3-0616-0410-b53a-bc976706d245

Console/Mainpage.dox
Console/ParsedCommand.cti
Console/ParsedCommand.hh
Console/ParsedCommand.ih
Console/ParsedCommand.mpp
Console/ParsedCommand.test.cc

index 6929f25..d1de4fe 100644 (file)
     </pre>
     \endhtmlonly
 
+
+    \subsection console_boostfn Adding non-function-pointer callable objects
+
+    It is possible to add other callable objects besides function (and member-function)
+    pointers. However, since it is not possible to automatically deduce the argument and return
+    types in this case, the callables have to be wrapped in a \c boost::function object:
+
+    \code
+    senf::console::root()
+        .add("test8", 
+             boost::function<void (std::ostream &, std::string const &)>(
+                 boost::bind(&fun3, _1, 4u, _2)));
+    \endcode
+
+    This works with any callable object where argument types cannot be deduced automatically:
+    Boost.Bind expressions, Boost.Lambda expressions, functors and so on.
+    
+    \htmlonly
+    <pre>
+    server:/$ test8 ok
+    ok
+    ok
+    ok
+    ok
+    server:/$ help test8
+    Usage:
+        test8 arg11:string
+    server:/$
+    </pre>
+    \endhtmlonly
+
+
     \subsection console_attr_summary Attribute summary
 
     Here a summary of the most common attributes
index 029fa2d..1aa2020 100644 (file)
@@ -331,7 +331,16 @@ ParsedArgumentAttributor(Overload & overload)
 ///////////////////////////////////////////////////////////////////////////
 // namespace members
 
-namespace {
+namespace senf {
+namespace console {
+namespace detail {
+
+    struct ParsedCommandAddNodeAccess
+    {
+        template <class Attributor, class Node>
+        static Attributor attributor(Node & node)
+            { return Attributor(node); }
+    };
 
     // What is THIS about ??
 
@@ -347,14 +356,16 @@ namespace {
     // If however, it does NOT take an std::ostream first argument, 'ignoreOneArg' will be true and
     // the create member will use boost::bind to DROP the first argument.
     
-    template <class Traits, bool ignoreOneArg, unsigned arity=Traits::arity>
+    template <class Traits, 
+              bool ignoreOneArg=! Traits::has_ostream_arg, 
+              unsigned arity=Traits::traits::arity>
     struct CreateParsedCommandOverload
     {};
 
     template <class Traits, unsigned arity>
     struct CreateParsedCommandOverload<Traits, false, arity>
     {
-        typedef Traits traits;
+        typedef typename Traits::traits traits;
         
         template <class Function>
         static typename senf::console::ParsedCommandOverload<traits>::ptr create(Function fn) 
@@ -366,51 +377,49 @@ namespace {
                                             4))
 #   include BOOST_PP_ITERATE()
 
-}
+    template <class Signature, class Fn>
+    typename senf::console::detail::ParsedCommandTraits<Signature>::Attributor
+    addOverloadedCommandNode(senf::console::DirectoryNode & node,  std::string const & name, Fn fn)
+    {
+        senf::console::OverloadedCommandNode & cmdNode (
+            node.hasChild(name) 
+            ? dynamic_cast<senf::console::OverloadedCommandNode &>(node(name))
+            : node.add(name, senf::console::OverloadedCommandNode::create()) );
+
+        typedef senf::console::detail::ParsedCommandTraits<Signature> CmdTraits;
+        typedef senf::console::ParsedCommandOverload<typename CmdTraits::traits> Overload;
+        typedef senf::console::ParsedArgumentAttributor<Overload> Attributor;
+
+        return senf::console::detail::ParsedCommandAddNodeAccess::attributor<Attributor>(
+            cmdNode.add( CreateParsedCommandOverload<CmdTraits>::create(fn) ) );
+    }
+
+}}}
 
 template <class Function>
-prefix_ senf::console::ParsedArgumentAttributor<
-    senf::console::ParsedCommandOverload<
-        typename senf::console::detail::ParsedCommandTraits<Function>::traits> >
+typename senf::console::detail::ParsedCommandTraits<Function>::Attributor
 senf::console::senf_console_add_node(DirectoryNode & node, std::string const & name,
                                      Function fn, int)
 {
-    OverloadedCommandNode & cmdNode (
-        node.hasChild(name) 
-        ? dynamic_cast<OverloadedCommandNode &>(node(name))
-        : node.add(name, OverloadedCommandNode::create()) );
-
-    typedef detail::ParsedCommandTraits<Function> CmdTraits;
-    typedef ParsedCommandOverload<typename CmdTraits::traits> Overload;
-    typedef ParsedArgumentAttributor<Overload> Attributor;
-
-    return Attributor(
-        cmdNode.add( CreateParsedCommandOverload<
-                         typename CmdTraits::traits, ! CmdTraits::has_ostream_arg>::create(fn) ) );
+    return senf::console::detail::addOverloadedCommandNode<Function>(node, name, fn);
+}
+
+template <class Signature>
+typename senf::console::detail::ParsedCommandTraits<Signature>::Attributor
+senf::console::senf_console_add_node(DirectoryNode & node, std::string const & name,
+                                     boost::function<Signature> fn, int)
+{
+    return senf::console::detail::addOverloadedCommandNode<Signature>(node, name, fn);
 }
 
 template <class Owner, class Function>
-prefix_ senf::console::ParsedArgumentAttributor<
-    senf::console::ParsedCommandOverload<
-        typename senf::console::detail::ParsedCommandTraits<Function>::traits> >
-senf::console::
-senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
-                      Function fn, int,
-                      typename boost::enable_if_c<detail::ParsedCommandTraits<Function>::is_member>::type *)
+typename senf::console::detail::ParsedCommandTraits<Function>::Attributor
+senf::console::senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
+                                     Function fn, int,
+                                     typename boost::enable_if_c<detail::ParsedCommandTraits<Function>::is_member>::type *)
 {
-    OverloadedCommandNode & cmdNode (
-        node.hasChild(name) 
-        ? dynamic_cast<OverloadedCommandNode &>(node(name))
-        : node.add(name, OverloadedCommandNode::create()) );
-
-    typedef detail::ParsedCommandTraits<Function> CmdTraits;
-    typedef ParsedCommandOverload<typename CmdTraits::traits> Overload;
-    typedef ParsedArgumentAttributor<Overload> Attributor;
-
-    return Attributor(
-        cmdNode.add( CreateParsedCommandOverload<
-                         typename CmdTraits::traits, ! CmdTraits::has_ostream_arg>::create(
-                             senf::membind(fn,&owner)) ) );
+    return senf::console::detail::addOverloadedCommandNode<Function>(
+        node, name, senf::membind(fn,&owner));
 }
 
 ///////////////////////////////cti.e///////////////////////////////////////
index a4ef298..ba32137 100644 (file)
@@ -171,8 +171,7 @@ namespace console {
 
         \todo Implement automatic binding of member functions for parser and formatter
      */
-    template <class FunctionTraits, class ReturnType=typename FunctionTraits::result_type, 
-              unsigned arity=FunctionTraits::arity>
+    template <class FunctionTraits, class ReturnType, unsigned arity>
     class ParsedCommandOverload : public ParsedCommandOverloadBase
     {
     public:
@@ -329,42 +328,32 @@ namespace console {
                                              assigned in the following way:
                                              <table class="senf fixedwidth">
                                              <tr>
-                                               <td><tt>command 1 2</tt></td>
-                                               <td>SyntaxErrorException: invalid number of
+                                               <td style="whitespace:no-wrap"><tt>command 1 2</tt></td>
+                                               <td colspan="5">SyntaxErrorException: invalid number of
                                                  arguments</td>
                                              </tr>
                                              <tr>
-                                               <td><tt>command 1 2 3</tt></td>
-                                               <td>\a a = 1, \n
-                                                   \a b = \e default, \n
-                                                   \a c = 2, \n
-                                                   \a d = \e default, \n
-                                                   \a e = 3</td>
+                                               <td style="white-space:nowrap"><tt>command 1 2 3</tt></td>
+                                               <td style="width:6em">\a a = 1</td><td style="width:6em">\a b = \e default</td><td style="width:6em">\a c = 2</td><td style="width:6em">\a d = \e default</td><td style="width:6em">\a e = 3</td>
                                              </tr>
                                              <tr>
-                                               <td><tt>command 1 2 3 4</tt></td>
-                                               <td>\a a = 1, \n
-                                                   \a b = 2, \n
-                                                   \a c = 3, \n
-                                                   \a d = \e default, \n
-                                                   \a e = 4</td>
+                                               <td style="white-space:nowrap"><tt>command 1 2 3 4</tt></td>
+                                               <td>\a a = 1</td><td>\a b = 2</td><td>\a c = 3</td><td>\a d = \e default</td><td>\a e = 4</td>
                                              </tr>
                                              <tr>
-                                               <td><tt>command 1 2 3 4 5</tt></td>
-                                               <td>\a a = 1, \n
-                                                   \a b = 2, \n
-                                                   \a c = 3, \n
-                                                   \a d = 4, \n
-                                                   \a e = 5</td>
+                                               <td style="white-space:nowrap"><tt>command 1 2 3 4 5</tt></td>
+                                               <td>\a a = 1</td><td>\a b = 2</td><td>\a c = 3</td><td>\a d = 4</td><td>\a e = 5</td>
                                              </tr>
                                              <tr>
-                                               <td><tt>command 1 2 3 4 5 6</tt></td>
-                                               <td>SyntaxErrorException: invalid number of
+                                               <td style="white-space:nowrap"><tt>command 1 2 3 4 5 6</tt></td>
+                                               <td colspan="5">SyntaxErrorException: invalid number of
                                                  arguments</td>
                                              </tr>
                                              </table>
-                                             So, if you assign default values as you are used to
-                                             they will work like in C++ and most other languages */
+                                             So, if you use default values as you are used to,
+                                             assigning default values to consecutive trailing
+                                             arguments, they work like they do in C++ and most other
+                                             languages */
         BOOST_PARAMETER_KEYWORD(type, type_name) ///< Type name of this arguments type
                                         /**< By default, the type of an argument is extracted from
                                              the C++ type name by taking the last component of the
@@ -413,7 +402,18 @@ namespace console {
     public:
         Self doc(std::string const & doc) const; ///< Set documentation for all overloads
         Self overloadDoc(std::string const & doc) const; ///< Set overload specific documentation
-        Self formatter(typename Overload::Formatter f) const; ///< Set return value formatter
+        Self formatter(typename Overload::Formatter formatter) const; 
+                                        ///< Set return value formatter
+                                        /**< This member is only available, if the \a ReturnType of
+                                             the installed callback is not \c void.
+
+                                             If \a ReturnType is not \c void, the \a formatter must
+                                             be a callable with a signature compatible with
+                                             \code
+                                             void formatter(ReturnType const & value, std::ostream & os);
+                                             \endcode
+                                             The \a formatter takes the return value of the call \a
+                                             value and writes it properly formated to \a os. */
 
     protected:
         ParsedArgumentAttributorBase(Overload & overload, unsigned index);
@@ -449,9 +449,7 @@ namespace console {
 
         \see \ref console_autoparse
      */
-    template < class Overload, 
-               unsigned index=0, 
-               bool flag=(index < unsigned(Overload::traits::arity)) >
+    template < class Overload, unsigned index, bool flag>
     class ParsedArgumentAttributor
         : public ParsedArgumentAttributorBase< Overload, 
                                                 ParsedArgumentAttributor<Overload, index, flag> >
@@ -490,7 +488,8 @@ namespace console {
                                              
                                              Each call to arg() will increment the argument index
                                              and advance to the next argument. This member is only
-                                             present, if there is an argument at the current. */
+                                             present, if there is an argument at the current
+                                             index. */
 
 #ifndef DOXYVEN
 
@@ -544,22 +543,7 @@ namespace console {
         template <class O, unsigned i, bool f>
         friend class ParsedArgumentAttributor;
 
-#ifndef DOXYGEN
-        
-        template <class Function>
-        friend ParsedArgumentAttributor<
-            ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
-        senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int);
-        
-        template <class Owner, class Function>
-        friend ParsedArgumentAttributor<
-            ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
-        senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
-                              Function fn, int,
-                              typename boost::enable_if_c<
-                                  detail::ParsedCommandTraits<Function>::is_member>::type * = 0);
-
-#endif
+        friend class detail::ParsedCommandAddNodeAccess;
     };
 
 #ifndef DOXYGEN
@@ -578,29 +562,20 @@ namespace console {
 
         template <class O, unsigned i, bool f>
         friend class ParsedArgumentAttributor;
-        
-        template <class Function>
-        friend ParsedArgumentAttributor<
-            ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
-        senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int);
-        
-        template <class Owner, class Function>
-        friend ParsedArgumentAttributor<
-            ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
-        senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
-                              Function fn, int,
-                              typename boost::enable_if_c<
-                                  detail::ParsedCommandTraits<Function>::is_member>::type * = 0);
+
+        friend class detail::ParsedCommandAddNodeAccess;
     };
 
     template <class Function>
-    ParsedArgumentAttributor<
-        ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
+    typename detail::ParsedCommandTraits<Function>::Attributor
     senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int);
 
+    template <class Signature>
+    typename detail::ParsedCommandTraits<Signature>::Attributor
+    senf_console_add_node(DirectoryNode & node, std::string const & name, boost::function<Signature> fn, int);
+
     template <class Owner, class Function>
-    ParsedArgumentAttributor<
-        ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
+    typename detail::ParsedCommandTraits<Function>::Attributor
     senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
                           Function fn, int,
                           typename boost::enable_if_c<
index d4a89f8..c2d887b 100644 (file)
 
 namespace senf {
 namespace console {
+
+    template < class FunctionTraits, 
+               class ReturnType=typename FunctionTraits::result_type, 
+              unsigned arity=FunctionTraits::arity >
+    class ParsedCommandOverload;
+
+    template < class Overload, 
+               unsigned index=0, 
+               bool flag=(index < unsigned(Overload::traits::arity)) >
+    class ParsedArgumentAttributor;
+
 namespace detail {
 
 #ifndef DOXYGEN
@@ -90,10 +101,10 @@ namespace detail {
     struct ParsedCommandTraits
     {};
 
-    template <class Function>
-    struct ParsedCommandTraits<Function, true>
+    template <class Fn>
+    struct ParsedCommandTraits<Fn, true>
     {
-        typedef Function base_type;
+        typedef Fn base_type;
         typedef typename senf::remove_any_pointer<base_type>::type function_type;
         typedef boost::function_traits<function_type> base_traits;
         typedef typename FirstArgType<base_traits>::type first_arg_type;
@@ -111,8 +122,13 @@ namespace detail {
         static const bool is_member = boost::is_member_pointer<base_type>::value;
         
         typedef typename senf::member_class<base_type>::type class_type;
+
+        typedef ParsedCommandOverload<traits> Overload;
+        typedef ParsedArgumentAttributor<Overload> Attributor;
     };
 
+    struct ParsedCommandAddNodeAccess;
+
 #endif
 
 }}}
index bfa3ef3..ede6974 100644 (file)
@@ -259,7 +259,7 @@ v_execute(std::ostream & os, ParseCommandInfo const & command)
 template <class Traits>
 struct CreateParsedCommandOverload<Traits, true, BOOST_PP_ITERATION()>
 {
-    typedef Traits traits;
+    typedef typename Traits::traits traits;
 
     template <class Function>
     static typename senf::console::ParsedCommandOverload<traits>::ptr create(Function fn)
index a9e47a4..223a3e3 100644 (file)
@@ -50,11 +50,8 @@ namespace {
 
     struct TestParser 
     {
-        typedef senf::console::ParseCommandInfo::TokensRange const & first_argument_type;
-        typedef std::string & second_argument_type;
-        typedef void result_type;
-
-        result_type operator()(first_argument_type, second_argument_type out) const
+        void operator()(senf::console::ParseCommandInfo::TokensRange const &,
+                        std::string & out) const
             { out = "true"; }
     };
 
@@ -144,7 +141,9 @@ BOOST_AUTO_UNIT_TEST(parsedCommand)
     {
         std::stringstream ss;
 
-        dir.add("cb7", &cb2)
+        // This tests adding boost::function objects and at the same time validates, that 
+        // compatible types also work
+        dir.add("cb7", boost::function<float()>(&cb2))
             .formatter( &testFormatter );
         BOOST_CHECK_NO_THROW(
             parser.parse("test/cb7",