X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FParsedCommand.hh;h=af8dfe7041afd67084bb21dc28de9a40d5c1d614;hb=80c6cb7ba9ad7776824c84809f422209adf27331;hp=14d63a02ce173d1e8956ba35dad042bc1b2be209;hpb=01e02e262ea327678dfa0c3f7a66d50ed91924d5;p=senf.git diff --git a/Console/ParsedCommand.hh b/Console/ParsedCommand.hh index 14d63a0..af8dfe7 100644 --- a/Console/ParsedCommand.hh +++ b/Console/ParsedCommand.hh @@ -27,17 +27,19 @@ #define HH_ParsedCommand_ 1 // Custom includes + +#define BOOST_PARAMETER_MAX_ARITY 6 + #include #include #include -#include #include #include #include #include #include "../config.hh" #include "OverloadedCommand.hh" -#include "ParseParameter.hh" +#include "Traits.hh" #include "../Utils/type_traits.hh" #include "ParsedCommand.ih" @@ -47,57 +49,172 @@ 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(&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(&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 ptr; - detail::ParameterInfoBase & arg(unsigned n) const; - template detail::ParameterInfo & arg(unsigned n) const; + detail::ArgumentInfoBase & arg(unsigned n) const; void doc(std::string const & d); protected: ParsedCommandOverloadBase(); - template - void addParameter(); + template void addParameter(); private: virtual unsigned v_numArguments() const; virtual void v_argumentDoc(unsigned index, ArgumentDoc & doc) const; virtual std::string v_doc() const; - typedef std::vector Parameters; + typedef std::vector Parameters; Parameters parameters_; std::string doc_; }; - template - class ParsedCommandOverload {}; + /** \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. + + \todo Implement automatic binding of member functions for parser and formatter + */ + template + class ParsedCommandOverload : public ParsedCommandOverloadBase + { + public: + typedef boost::intrusive_ptr 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; - operator OverloadedCommandNode & () const; + 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 void defaultValue(Type const & value) const; + void typeName(std::string const & doc) const; + void defaultDoc(std::string const & doc) const; ParsedCommandOverloadBase & overload() const; void overloadDoc(std::string const & doc) const; - void nodeDoc(std::string const & doc) const; private: @@ -105,140 +222,356 @@ namespace console { 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 ParsedCommandAttributor : public ParsedCommandAttributorBase { public: - Overload & overload() const; + Overload & overload() const; ///< Get the command overload protected: ParsedCommandAttributor(Overload & overload, unsigned index); private: }; - - namespace tag { - BOOST_PARAMETER_KEYWORD(detail, name_); - BOOST_PARAMETER_KEYWORD(detail, description_); - BOOST_PARAMETER_KEYWORD(detail, default_value_); + + /** \brief Keyword argument tags + + The tags defined in this namespace are used as keyword arguments via the Boost.Parameter + 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 using namespace 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 +
+        Usage:
+            command [name:type_name]
+        
+        With:
+            name      description
+                default: default_doc
+        
+ \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: +
+                                             command a:int [b:int] c:int [d:int] e:int
+                                             
+ When calling the overload, the arguments will be + assigned in the following way: + + + + + + + + + + + + + + + + + + + + + +
command 1 2SyntaxErrorException: invalid number of + arguments
command 1 2 3\a a = 1\a b = \e default\a c = 2\a d = \e default\a e = 3
command 1 2 3 4\a a = 1\a b = 2\a c = 3\a d = \e default\a e = 4
command 1 2 3 4 5\a a = 1\a b = 2\a c = 3\a d = 4\a e = 5
command 1 2 3 4 5 6SyntaxErrorException: invalid number of + arguments
+ 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 + 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. */ } - template - class ParsedAttributeAttributorBase + /** \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 ParsedArgumentAttributorBase : public ParsedCommandAttributor { public: - Self doc(std::string const & doc) const; - Self overloadDoc(std::string const & doc) const; + 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 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: - ParsedAttributeAttributorBase(Overload & overload, unsigned index); + ParsedArgumentAttributorBase(Overload & overload, unsigned index); private: }; - template < class Overload, - unsigned index=0, - bool flag=(index < unsigned(Overload::traits::arity)) > - class ParsedAttributeAttributor - : public ParsedAttributeAttributorBase< Overload, - ParsedAttributeAttributor > +#ifndef DOXYGEN + + template + class ParsedArgumentAttributorBase + : public ParsedCommandAttributor { public: - 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 ParsedAttributeAttributor next_type; + Self doc(std::string const & doc) const; ///< Set documentation for all overloads + Self overloadDoc(std::string const & doc) const; ///< Set overload specific documentation - typedef OverloadedCommandNode node_type; - typedef ParsedAttributeAttributor return_type; + protected: + ParsedArgumentAttributorBase(Overload & overload, unsigned index); + + private: + }; +#endif + + /** \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, bool flag> + class ParsedArgumentAttributor + : public ParsedArgumentAttributorBase< Overload, + ParsedArgumentAttributor > + { typedef boost::parameter::parameters< - tag::detail::name_, - tag::detail::description_, - tag::detail::default_value_> arg_params; + kw::type::name, + kw::type::description, + kw::type::default_value, + kw::type::type_name, + kw::type::default_doc, + kw::type::parser> arg_params; - next_type arg() const; + public: + typedef OverloadedCommandNode node_type; + typedef ParsedArgumentAttributor return_type; -# define BOOST_PP_ITERATION_PARAMS_1 \ - (4, (1, 3, SENF_ABSOLUTE_INCLUDE_PATH(Console/ParsedCommand.mpp), 5)) + 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 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 Boost.Parameter + 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 + index. */ + +#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 ParsedAttributeAttributor(Overload & overload); + explicit ParsedArgumentAttributor(Overload & overload); template next_type argInfo(ArgumentPack const & args) const; + template + void argInfo(Kw const &, ArgumentPack const &, boost::mpl::false_) + const; template - next_type argInfo(ArgumentPack const & args, boost::mpl::true_) const; + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::true_) + const; template - next_type argInfo(ArgumentPack const & args, boost::mpl::false_) const; - - next_type argInfo(std::string const & name, std::string const & doc) const; - next_type argInfo(std::string const & name, std::string const & doc, - value_type const & value) const; + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::true_) + const; + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::true_) + const; + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::true_) + const; + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::true_) + const; + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::true_) + const; - ParsedAttributeAttributor next() const; + next_type next() const; void defaultValue(value_type const & value) const; + template void parser(Fn fn) const; template - friend class ParsedAttributeAttributor; - - template - friend ParsedAttributeAttributor< - ParsedCommandOverload::traits> > - senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int); - - template - friend ParsedAttributeAttributor< - ParsedCommandOverload::traits> > - senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name, - Function fn, int, - typename boost::enable_if_c< - detail::ParsedCommandTraits::is_member>::type * = 0); + friend class ParsedArgumentAttributor; + + friend class detail::ParsedCommandAddNodeAccess; }; +#ifndef DOXYGEN + template - class ParsedAttributeAttributor - : public ParsedCommandAttributor< Overload > + class ParsedArgumentAttributor + : public ParsedArgumentAttributorBase< Overload, + ParsedArgumentAttributor > { public: typedef OverloadedCommandNode node_type; - typedef ParsedAttributeAttributor return_type; + typedef ParsedArgumentAttributor return_type; private: - explicit ParsedAttributeAttributor(Overload & overload); + explicit ParsedArgumentAttributor(Overload & overload); template - friend class ParsedAttributeAttributor; - - template - friend ParsedAttributeAttributor< - ParsedCommandOverload::traits> > - senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int); - - template - friend ParsedAttributeAttributor< - ParsedCommandOverload::traits> > - senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name, - Function fn, int, - typename boost::enable_if_c< - detail::ParsedCommandTraits::is_member>::type * = 0); - }; + friend class ParsedArgumentAttributor; -#ifndef DOXYGEN + friend class detail::ParsedCommandAddNodeAccess; + }; template - ParsedAttributeAttributor< - ParsedCommandOverload::traits> > + typename detail::ParsedCommandTraits::Attributor senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int); + template + typename detail::ParsedCommandTraits::Attributor + senf_console_add_node(DirectoryNode & node, std::string const & name, boost::function fn, int); + template - ParsedAttributeAttributor< - ParsedCommandOverload::traits> > + typename detail::ParsedCommandTraits::Attributor senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name, Function fn, int, typename boost::enable_if_c< @@ -251,7 +584,7 @@ namespace console { #include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP() BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedCommandOverload, (class,unsigned)) -BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedAttributeAttributor, (class, unsigned, bool)) +BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedArgumentAttributor, (class, unsigned, bool)) BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function_traits, 1) ///////////////////////////////hh.e////////////////////////////////////////