X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FParsedCommand.hh;h=af8dfe7041afd67084bb21dc28de9a40d5c1d614;hb=80c6cb7ba9ad7776824c84809f422209adf27331;hp=b2fca486797fad5570eb2cf26884b39abc0cec4d;hpb=748363b235c4651b3992cc291a95dc2e18e45724;p=senf.git diff --git a/Console/ParsedCommand.hh b/Console/ParsedCommand.hh index b2fca48..af8dfe7 100644 --- a/Console/ParsedCommand.hh +++ b/Console/ParsedCommand.hh @@ -27,15 +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" @@ -45,47 +49,533 @@ 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::ArgumentInfoBase & arg(unsigned n) const; + + void doc(std::string const & d); + protected: ParsedCommandOverloadBase(); - detail::ParameterInfoBase & arg(unsigned n) const; - template detail::ParameterInfo & arg(unsigned n) const; - - template - void addParameter(); + template 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 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; ///< 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; + 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: + 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 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 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. */ + } + + /** \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; ///< 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: + ParsedArgumentAttributorBase(Overload & overload, unsigned index); + + private: + }; + #ifndef DOXYGEN + template + class ParsedArgumentAttributorBase + : public ParsedCommandAttributor + { + 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: + }; + +#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< + 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 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 ParsedArgumentAttributor(Overload & overload); + + template + next_type argInfo(ArgumentPack const & args) const; + template + void argInfo(Kw const &, ArgumentPack const &, boost::mpl::false_) + 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; + 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; + + next_type next() const; + + void defaultValue(value_type const & value) const; + template void parser(Fn fn) const; + + template + friend class ParsedArgumentAttributor; + + friend class detail::ParsedCommandAddNodeAccess; + }; + +#ifndef DOXYGEN + + template + class ParsedArgumentAttributor + : public ParsedArgumentAttributorBase< Overload, + ParsedArgumentAttributor > + { + public: + typedef OverloadedCommandNode node_type; + typedef ParsedArgumentAttributor return_type; + + private: + explicit ParsedArgumentAttributor(Overload & overload); + + template + friend class ParsedArgumentAttributor; + + friend class detail::ParsedCommandAddNodeAccess; + }; + template - 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 - 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::is_member>::type * = 0); + typename boost::enable_if_c< + detail::ParsedCommandTraits::is_member>::type * = 0); #endif @@ -94,6 +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::ParsedArgumentAttributor, (class, unsigned, bool)) BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function_traits, 1) ///////////////////////////////hh.e////////////////////////////////////////