X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FParsedCommand.hh;h=9f43d157be19f8ba6bb68d5f49db700849a021ce;hb=259da4c692259311c6ec99566b57f5ed1e68e93e;hp=70f4203a855883a5a027fb375ca94181f53d8ead;hpb=5209d7573f53f4b32730833af00668f5c0e56147;p=senf.git diff --git a/Console/ParsedCommand.hh b/Console/ParsedCommand.hh index 70f4203..9f43d15 100644 --- a/Console/ParsedCommand.hh +++ b/Console/ParsedCommand.hh @@ -33,9 +33,11 @@ #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,14 +47,95 @@ 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; + template detail::ArgumentInfo & arg(unsigned n) const; void doc(std::string const & d); @@ -67,30 +150,282 @@ namespace console { 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_; }; + /** \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 ParsedCommandOverload {}; + 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. + */ + 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 void defaultValue(Type const & value) 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. + + \fixme Implement compile-time checking, that after a defaulted arg only defaulted args are + allowed. + */ + 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: + \li Either qualify them with their complete namespace: arg( senf::console::kw::name = + "name" ) + \li or use a namespace alias: namespace kw = senf::console::kw; arg( kw::name = "name" + ); + \li import the keywords into your namespace: using namespace senf::console::kw; arg( + name = "name"); + + 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. + */ + namespace kw { + BOOST_PARAMETER_KEYWORD(type, name) ///< Argument name + BOOST_PARAMETER_KEYWORD(type, description) ///< One-line Argument description + BOOST_PARAMETER_KEYWORD(type, default_value) ///< Argument default value + } + + /** \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. + */ + 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: + }; + + + /** \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. + */ + template < class Overload, + unsigned index=0, + bool flag=(index < unsigned(Overload::traits::arity)) > + class ParsedArgumentAttributor + : public ParsedArgumentAttributorBase< Overload, + ParsedArgumentAttributor > + { + typedef boost::parameter::parameters< + kw::type::name, + kw::type::description, + kw::type::default_value> 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. The valid keywords are defined in the + senf::console::kw namespace. + + This member is only present, if there is an argument at + the current index. */ + +#ifndef DOXYVEN + +# define BOOST_PP_ITERATION_PARAMS_1 \ + (4, (1, 3, 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::true_) + const; + + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::false_) + const; + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::false_) + const; + template + void argInfo(boost::parameter::keyword const &, + ArgumentPack const & args, boost::mpl::false_) + const; + + next_type next() const; + + void defaultValue(value_type const & value) const; + + template + friend class ParsedArgumentAttributor; + #ifndef DOXYGEN + + template + friend ParsedArgumentAttributor< + ParsedCommandOverload::traits> > + senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int); + + template + friend ParsedArgumentAttributor< + 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); + +#endif + }; + +#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; + + template + friend ParsedArgumentAttributor< + ParsedCommandOverload::traits> > + senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int); + + template + friend ParsedArgumentAttributor< + 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); + }; template - ParsedCommandOverload::traits> & + ParsedArgumentAttributor< + ParsedCommandOverload::traits> > senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int); template - ParsedCommandOverload::traits> & + ParsedArgumentAttributor< + ParsedCommandOverload::traits> > 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 @@ -99,6 +434,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////////////////////////////////////////