Console: Overhaul documentation
[senf.git] / Console / ParsedCommand.hh
index b2fca48..75724a8 100644 (file)
 #define HH_ParsedCommand_ 1
 
 // Custom includes
+
+#define BOOST_PARAMETER_MAX_ARITY 6
+
 #include <vector>
 #include <boost/type_traits/function_traits.hpp>
 #include <boost/type_traits/is_member_pointer.hpp>
-#include <boost/type_traits/is_same.hpp>
 #include <boost/mpl/if.hpp>
 #include <boost/utility.hpp>
+#include <boost/parameter/keyword.hpp>
+#include <boost/parameter/parameters.hpp>
 #include "../config.hh"
 #include "OverloadedCommand.hh"
-#include "ParseParameter.hh"
+#include "Traits.hh"
 #include "../Utils/type_traits.hh"
 
 #include "ParsedCommand.ih"
 namespace senf {
 namespace console {
 
+    namespace detail { class ArgumentInfoBase; }
+
+    /** \brief CommandOverload implementation with automatic argument parsing
+
+        ParsedCommandOverloadBase implements a CommandOverload implementation supporting automatic
+        parsing of arguments. This is \e not a node, it's a CommandOverload which is then added to
+        an OverloadedCommandNode instance.
+
+        Automatic argument parsing and return value processing consists of several components:
+        \li \ref overload_add Adding overload instances to the tree
+        \li (Advanced) \ref overload_parse
+        \li (Advanced) \ref overload_format
+
+        \section overload_add Adding argument parsing callbacks to the tree
+
+        Adding appropriate callbacks to the tree is very simple: just path a function pointer to
+        DirectoryNode::add() or a member function pointer to ScopedDirectory::add().
+        \code
+        std::string taskStatus(int id);
+
+        senf::console::root().add("taskStatus", &taskStatus);
+        \endcode
+
+        There are quite a number of additional parameters available to be set. These parameters are
+        documented in ParsedArgumentAttributor. Parameters are set by adding them as additional
+        calls after adding the node:
+
+        \code
+        senf::console::root().add("taskStatus", &taskStatus)
+            .doc("Query the current task status")
+            .arg( name = "id",
+                  description = "numeric id of task to check, -1 for the current task."
+                  default_value = -1 );
+        \endcode
+
+        You may also add an additional \c std::ostream & Argument as first argument to the
+        callback. If this argument is present, the stream connected to the console which issued the
+        command will be passed there. This allows writing arbitrary messages to the console.
+
+        Additionally, overloading is supported by registering multiple commands under the same
+        name. So, elaborating on above example:
+        \code
+        std::string taskStatus(int id);
+        std::string taskStatus(std::string const & name);
+
+        senf::console::root()
+            .add("taskStatus", static_cast<std::string (*)(int)>(&taskStatus))
+            .doc("Query the current task status")
+            .overloadDoc("Query status by id")
+            .arg( name = "id",
+                  description = "numeric id of task to check, -1 for the current task."
+                  default_value = -1 );
+        senf::console::root()
+            .add("taskStatus", static_cast<std::string (*)(std::string const &)>(&taskStatus))
+            .overloadDoc("Query status by name")
+            .arg( name = "name",
+                  description = "name of task to check" );
+        \endcode
+
+        We can see here, that taking the address of an overloaded function requires a cast. If you
+        can give unique names to each of the C++ overloads (not the overloads in the console), you
+        should do so to make the unwieldy casts unnecessary.
+
+        \section overload_parse Custom parameter parsers
+        
+        By default, parameters are parsed using \c boost::lexical_cast and therefore using \c
+        iostreams. This means, that any type which can be read from a stream can automatically be
+        used as argument type.
+
+        However, argument parsing can be configured by specializing
+        senf::console::ArgumentTraits. See that class for more information.
+
+        \section overload_format Custom return-value formatters
+
+        By default, return values are streamed to an ostream. This automatically allows any
+        streamable type to be used as return value. To add new types or customize the formating, the
+        senf::console::ReturnValueTraits template needs to be specialized for that type. See
+        that class for more information.
+
+        \ingroup console_commands
+     */
     class ParsedCommandOverloadBase
         : public CommandOverload
     {
     public:
         typedef boost::intrusive_ptr<ParsedCommandOverloadBase> ptr;
 
+        detail::ArgumentInfoBase & arg(unsigned n) const;
+        template <class Type> detail::ArgumentInfo<Type> & arg(unsigned n) const;
+
+        void doc(std::string const & d);
+
     protected:
         ParsedCommandOverloadBase();
 
-        detail::ParameterInfoBase & arg(unsigned n) const;
-        template <class Type> detail::ParameterInfo<Type> & arg(unsigned n) const;
-
         template <class Type>
         void addParameter();
 
     private:
-        virtual void v_help(std::ostream & os) const;
+        virtual unsigned v_numArguments() const;
+        virtual void v_argumentDoc(unsigned index, ArgumentDoc & doc) const;
+        virtual std::string v_doc() const;
 
-        typedef std::vector<detail::ParameterInfoBase::ptr> Parameters;
+        typedef std::vector<detail::ArgumentInfoBase::ptr> Parameters;
         Parameters parameters_;
+        std::string doc_;
     };
 
+    /** \brief Parsed command overload
+
+        ParsedCommandOverload provides the command overload added to an OverloadedCommandNode for an
+        automatically parsed command.
+
+        This class is normally instantiated automatically when adding a function or member-function
+        pointer as callback to the tree. Manually instantiation this type of overload is \e not
+        simple, since the function signature has to be manipulated correctly to support the optional
+        \c std::ostream first argument.
+
+        \implementation This class is specialized for each supported number of command arguments.
+     */
     template <class FunctionTraits, unsigned arity=FunctionTraits::arity>
-    class ParsedCommandOverload {};
+    class ParsedCommandOverload : public ParsedCommandOverloadBase
+    {
+    public:
+        typedef boost::intrusive_ptr<ParsedCommandOverload> ptr;
+
+#ifdef DOXYGEN
+        static ptr create(Function fn);
+#endif
+    };
+
+#ifndef DOXYGEN
 
 #   define BOOST_PP_ITERATION_PARAMS_1 (4, (0, SENF_CONSOLE_MAX_COMMAND_ARITY,                     \
                                             SENF_ABSOLUTE_INCLUDE_PATH(Console/ParsedCommand.mpp), \
                                             1))
 #   include BOOST_PP_ITERATE()
 
+#endif
+
+    /** \brief Generic ParsedCommandOverladBase attributes
+
+        Attributes for parsed commands are not set directly on the node. They are set via a special
+        attributor temporary returned when adding a parsed command to the tree.
+        
+        This class is the base class for those attributors. It provides members which do not depend
+        in any way on the exact type of command added.
+
+        \see \ref console_autoparse
+     */
+    class ParsedCommandAttributorBase
+    {
+    public:
+        OverloadedCommandNode & node() const; ///< Return the node object
+        operator OverloadedCommandNode & () const; ///< Automatically convert to node object
+
+    protected:
+        ParsedCommandAttributorBase(ParsedCommandOverloadBase & overload, unsigned index);
+
+        void argName(std::string const & name) const;
+        void argDoc(std::string const & doc) const;
+        template <class Type> void defaultValue(Type const & value) const;
+        void typeName(std::string const & doc) const;
+        void defaultDoc(std::string const & doc) const;
+        template <class Type, class Fn> void parser(Fn fn) const;
+
+        ParsedCommandOverloadBase & overload() const;
+        void overloadDoc(std::string const & doc) const;
+
+        void nodeDoc(std::string const & doc) const;
+        
+    private:
+        ParsedCommandOverloadBase & overload_;
+        unsigned index_;
+    };
+
+    /** \brief Non argument dependent ParsedCommandBase attributes 
+        
+        Attributes for parsed commands are not set directly on the node. They are set via a special
+        attributor temporary returned when adding a parsed command to the tree.
+
+        This class adds all those members, which do depend on the type of command added (and thereby
+        on that commands signature) but do not depend on the type of any single argument.
+
+        \see \ref console_autoparse
+     */
+    template <class Overload>
+    class ParsedCommandAttributor
+        : public ParsedCommandAttributorBase
+    {
+    public:
+        Overload & overload() const;    ///< Get the command overload
+
+    protected:
+        ParsedCommandAttributor(Overload & overload, unsigned index);
+
+    private:
+    };
+
+    /** \brief Keyword argument tags
+
+        The tags defined in this namespace are used as keyword arguments via the <a
+        href="http://www.boost.org/doc/libs/1_33_1/libs/parameter/doc/html/index.html">Boost.Parameter</a>
+        library.
+
+        For the keyword tags, the standard C++ scoping rules apply
+        \code
+        // Either qualify them with their complete namespace
+        dir.add(...)
+            .arg( senf::console::kw::name = "name" );
+        
+        // Or use a namespace alias
+        namespace kw = senf::console::kw;
+        dir.add(...)
+            .arg( kw::name = "name" );
+
+        // Or import the keywords into the current namespace (beware of name collisions)
+        using namespace senf::console::kw;
+        dir.add(...)
+            .arg( name = "name" );
+        \endcode
+
+        The second alternative is preferred, the <tt>using namespace</tt> directive may be used as
+        long as the keyword names do not clash with another visible symbol.
+
+        \section kw_attributes Argument attribute values
+
+        The keywords are used to set argument attributes.  The keywords \ref default_value and \ref
+        parser influence, how an argument is parsed/interpreted whereas \ref name, \ref description,
+        \ref type_name and \ref default_doc are used to change the arguments documentation:
+        \code
+        void command(int);
+
+        dir.add("command", &command)
+            .arg( kw::name          = "name",
+                  kw::description   = "description",
+                  kw::default_value = 1,
+                  kw::type_name     = "type_name",
+                  kw::default_doc   = "default_doc" );
+        \endcode
+        Will create the following documentation:
+        \htmlonly
+        <pre>
+        Usage:
+            command [name:type_name]
+        
+        With:
+            name      description
+                default: default_doc
+        </pre>
+        \endhtmlonly
+
+        \see \ref senf::console::ParsedArgumentAttributor::arg()
+     */
+    namespace kw {
+        BOOST_PARAMETER_KEYWORD(type, name) ///< Argument name
+                                        /**< Sets the displayed name of the argument. */
+        BOOST_PARAMETER_KEYWORD(type, description) ///< One-line argument description
+                                        /**< This description is shown in the argument
+                                             reference. If several overloads have same-named
+                                             arguments, only one of them should be documented. This
+                                             documentation then applies to all arguments of that
+                                             name. */
+        BOOST_PARAMETER_KEYWORD(type, default_value) ///< Argument default value
+                                        /**< If a default value is specified for an argument, that
+                                             argument is optional. If an overload is called with
+                                             fewer arguments than defined, optional values will be
+                                             used beginning at the last optional argument and going
+                                             forward until all arguments have values assigned. E.g.,
+                                             an overload with 5 parameters \a a - \a e with two
+                                             defaults attached:
+                                             <pre>
+                                             command a:int [b:int] c:int [d:int] e:int
+                                             </pre>
+                                             When calling the overload, the arguments will be
+                                             assigned in the following way:
+                                             <table class="senf fixedwidth">
+                                             <tr>
+                                               <td><tt>command 1 2</tt></td>
+                                               <td>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>
+                                             </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>
+                                             </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>
+                                             </tr>
+                                             <tr>
+                                               <td><tt>command 1 2 3 4 5 6</tt></td>
+                                               <td>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 */
+        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
+                                             fully scoped name. This value can be changed by setting
+                                             this attribute. */
+        BOOST_PARAMETER_KEYWORD(type, default_doc) ///< String rep of default value
+                                        /**< By default, the default value is documented by
+                                             converting the value to it's string representation
+                                             using \c boost::lexical_cast / \c iostreams. The
+                                             displayed value can be changed by setting this
+                                             attribute. */
+        BOOST_PARAMETER_KEYWORD(type, parser) ///< Argument parser
+                                        /**< The argument parser is used to convert the argument
+                                             token list returned by the console/config parser into
+                                             the appropriate value. If not set explicitly, this
+                                             conversion is supplied by the ArgumentTraits
+                                             class. 
+
+                                             Setting the \a parser attribute allows to use a custom
+                                             parser. The parser is an arbitrary callable object with
+                                             the signature
+                                             \code
+                                             void parser(senf::console::ParseCommandInfo::TokensRange const & tokens, value_type & out);
+                                             \endcode
+                                             where \c value_type is the type of the overload
+                                             parameter. The parser must read and parse the complete
+                                             \a tokens range and return the parsed value in \a
+                                             out. If the parser fails, it must raise a
+                                             senf::console::SyntaxErrorException. */
+    }
+
+    /** \brief Derived class dependent ParsedCommandBase attributes
+
+        Attributes for parsed commands are not set directly on the node. They are set via a special
+        attributor temporary returned when adding a parsed command to the tree.
+        
+        This class adds all those members, which do not depend on any specific argument but which
+        need to return the correct attributor type.
+
+        \see \ref console_autoparse
+     */
+    template <class Overload, class Self>
+    class ParsedArgumentAttributorBase
+        : public ParsedCommandAttributor<Overload>
+    {
+    public:
+        Self doc(std::string const & doc) const; ///< Set documentation for all overloads
+        Self overloadDoc(std::string const & doc) const; ///< Set overload specific documentation
+
+    protected:
+        ParsedArgumentAttributorBase(Overload & overload, unsigned index);
+
+    private:
+    };
+
+    
+    /** \brief Argument dependent ParsedCommandBase attributes
+
+        Attributes for parsed commands are not set directly on the node. They are set via a special
+        attributor temporary returned when adding a parsed command to the tree.
+        
+        This class adds all those members, which depend on a specific argument. Each call to \c arg
+        will advance to the next argument.
+
+        \see \ref console_autoparse
+     */
+    template < class Overload, 
+               unsigned index=0, 
+               bool flag=(index < unsigned(Overload::traits::arity)) >
+    class ParsedArgumentAttributor
+        : public ParsedArgumentAttributorBase< Overload, 
+                                                ParsedArgumentAttributor<Overload, index, flag> >
+    {
+        typedef boost::parameter::parameters<
+            kw::type::name,
+            kw::type::description,
+            kw::type::default_value,
+            kw::type::type_name,
+            kw::type::default_doc,
+            kw::type::parser> arg_params;
+
+    public:
+        typedef OverloadedCommandNode node_type;
+        typedef ParsedArgumentAttributor return_type;
+
+        typedef typename senf::function_traits_arg_type< 
+            typename Overload::traits, int(index) >::type arg_type;
+        typedef typename senf::remove_cvref< arg_type >::type value_type;
+        typedef ParsedArgumentAttributor<Overload, index+1> next_type;
+
+        next_type arg() const;          ///< Set argument attributes
+                                        /**< This member changes the attributes for the current
+                                             argument. The attributes are passed to arg() as keyword
+                                             arguments using the <a
+                                             href="http://www.boost.org/doc/libs/1_33_1/libs/parameter/doc/html/index.html">Boost.Parameter</a>
+                                             library. 
+                                             \code
+                                             ...
+                                                 .arg( kw::name          = "name", 
+                                                       kw::default_value = 1 )
+                                             ...
+                                             \endcode
+                                             The valid keywords are defined in the senf::console::kw
+                                             namespace.
+                                             
+                                             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. */
+
+#ifndef DOXYVEN
+
+#       define BOOST_PP_ITERATION_PARAMS_1                                                        \
+            (4, (1, BOOST_PARAMETER_MAX_ARITY,                                                    \
+                 SENF_ABSOLUTE_INCLUDE_PATH(Console/ParsedCommand.mpp),                           \
+                 5))
+#       include BOOST_PP_ITERATE()
+
+#endif
+
+    private:
+        explicit ParsedArgumentAttributor(Overload & overload);
+
+        template <class ArgumentPack>
+        next_type argInfo(ArgumentPack const & args) const;
+        template <class Kw, class ArgumentPack>
+        void argInfo(Kw const &, ArgumentPack const &, boost::mpl::false_) 
+            const;
+
+        template <class ArgumentPack>
+        void argInfo(boost::parameter::keyword<kw::type::name> const &, 
+                     ArgumentPack const & args, boost::mpl::true_) 
+            const;
+        template <class ArgumentPack>
+        void argInfo(boost::parameter::keyword<kw::type::description> const &, 
+                     ArgumentPack const & args, boost::mpl::true_) 
+            const;
+        template <class ArgumentPack>
+        void argInfo(boost::parameter::keyword<kw::type::default_value> const &, 
+                     ArgumentPack const & args, boost::mpl::true_) 
+            const;
+        template <class ArgumentPack>
+        void argInfo(boost::parameter::keyword<kw::type::type_name> const &, 
+                     ArgumentPack const & args, boost::mpl::true_) 
+            const;
+        template <class ArgumentPack>
+        void argInfo(boost::parameter::keyword<kw::type::default_doc> const &, 
+                     ArgumentPack const & args, boost::mpl::true_) 
+            const;
+        template <class ArgumentPack>
+        void argInfo(boost::parameter::keyword<kw::type::parser> const &, 
+                     ArgumentPack const & args, boost::mpl::true_) 
+            const;
+
+        next_type next() const;
+
+        void defaultValue(value_type const & value) const;
+        template <class Fn> void parser(Fn fn) const;
+
+        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
+    };
+
+#ifndef DOXYGEN
+
+    template <class Overload, unsigned index>
+    class ParsedArgumentAttributor<Overload, index, false>
+        : public ParsedArgumentAttributorBase< Overload, 
+                                                ParsedArgumentAttributor<Overload, index, false> >
+    {
+    public:
+        typedef OverloadedCommandNode node_type;
+        typedef ParsedArgumentAttributor return_type;
+
+    private:
+        explicit ParsedArgumentAttributor(Overload & overload);
+
+        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);
+    };
 
     template <class Function>
-    ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> &
+    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>
-    ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> &
+    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);
+                          typename boost::enable_if_c<
+                              detail::ParsedCommandTraits<Function>::is_member>::type * = 0);
 
 #endif
 
@@ -94,6 +593,7 @@ namespace console {
 #include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
 
 BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedCommandOverload, (class,unsigned))
+BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedArgumentAttributor, (class, unsigned, bool))
 BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function_traits, 1)
 
 ///////////////////////////////hh.e////////////////////////////////////////