Console: Implement custom return-value formatter support
[senf.git] / Console / ParsedCommand.hh
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief ParsedCommand public header */
25
26 #ifndef HH_ParsedCommand_
27 #define HH_ParsedCommand_ 1
28
29 // Custom includes
30
31 #define BOOST_PARAMETER_MAX_ARITY 6
32
33 #include <vector>
34 #include <boost/type_traits/function_traits.hpp>
35 #include <boost/type_traits/is_member_pointer.hpp>
36 #include <boost/mpl/if.hpp>
37 #include <boost/utility.hpp>
38 #include <boost/parameter/keyword.hpp>
39 #include <boost/parameter/parameters.hpp>
40 #include "../config.hh"
41 #include "OverloadedCommand.hh"
42 #include "Traits.hh"
43 #include "../Utils/type_traits.hh"
44
45 #include "ParsedCommand.ih"
46 #include "ParsedCommand.mpp"
47 ///////////////////////////////hh.p////////////////////////////////////////
48
49 namespace senf {
50 namespace console {
51
52     namespace detail { class ArgumentInfoBase; }
53
54     /** \brief CommandOverload implementation with automatic argument parsing
55
56         ParsedCommandOverloadBase implements a CommandOverload implementation supporting automatic
57         parsing of arguments. This is \e not a node, it's a CommandOverload which is then added to
58         an OverloadedCommandNode instance.
59
60         Automatic argument parsing and return value processing consists of several components:
61         \li \ref overload_add Adding overload instances to the tree
62         \li (Advanced) \ref overload_parse
63         \li (Advanced) \ref overload_format
64
65         \section overload_add Adding argument parsing callbacks to the tree
66
67         Adding appropriate callbacks to the tree is very simple: just path a function pointer to
68         DirectoryNode::add() or a member function pointer to ScopedDirectory::add().
69         \code
70         std::string taskStatus(int id);
71
72         senf::console::root().add("taskStatus", &taskStatus);
73         \endcode
74
75         There are quite a number of additional parameters available to be set. These parameters are
76         documented in ParsedArgumentAttributor. Parameters are set by adding them as additional
77         calls after adding the node:
78
79         \code
80         senf::console::root().add("taskStatus", &taskStatus)
81             .doc("Query the current task status")
82             .arg( name = "id",
83                   description = "numeric id of task to check, -1 for the current task."
84                   default_value = -1 );
85         \endcode
86
87         You may also add an additional \c std::ostream & Argument as first argument to the
88         callback. If this argument is present, the stream connected to the console which issued the
89         command will be passed there. This allows writing arbitrary messages to the console.
90
91         Additionally, overloading is supported by registering multiple commands under the same
92         name. So, elaborating on above example:
93         \code
94         std::string taskStatus(int id);
95         std::string taskStatus(std::string const & name);
96
97         senf::console::root()
98             .add("taskStatus", static_cast<std::string (*)(int)>(&taskStatus))
99             .doc("Query the current task status")
100             .overloadDoc("Query status by id")
101             .arg( name = "id",
102                   description = "numeric id of task to check, -1 for the current task."
103                   default_value = -1 );
104         senf::console::root()
105             .add("taskStatus", static_cast<std::string (*)(std::string const &)>(&taskStatus))
106             .overloadDoc("Query status by name")
107             .arg( name = "name",
108                   description = "name of task to check" );
109         \endcode
110
111         We can see here, that taking the address of an overloaded function requires a cast. If you
112         can give unique names to each of the C++ overloads (not the overloads in the console), you
113         should do so to make the unwieldy casts unnecessary.
114
115         \section overload_parse Custom parameter parsers
116         
117         By default, parameters are parsed using \c boost::lexical_cast and therefore using \c
118         iostreams. This means, that any type which can be read from a stream can automatically be
119         used as argument type.
120
121         However, argument parsing can be configured by specializing
122         senf::console::ArgumentTraits. See that class for more information.
123
124         \section overload_format Custom return-value formatters
125
126         By default, return values are streamed to an ostream. This automatically allows any
127         streamable type to be used as return value. To add new types or customize the formating, the
128         senf::console::ReturnValueTraits template needs to be specialized for that type. See
129         that class for more information.
130
131         \ingroup console_commands
132      */
133     class ParsedCommandOverloadBase
134         : public CommandOverload
135     {
136     public:
137         typedef boost::intrusive_ptr<ParsedCommandOverloadBase> ptr;
138
139         detail::ArgumentInfoBase & arg(unsigned n) const;
140         template <class Type> detail::ArgumentInfo<Type> & arg(unsigned n) const;
141
142         void doc(std::string const & d);
143
144     protected:
145         ParsedCommandOverloadBase();
146
147         template <class Type>
148         void addParameter();
149
150     private:
151         virtual unsigned v_numArguments() const;
152         virtual void v_argumentDoc(unsigned index, ArgumentDoc & doc) const;
153         virtual std::string v_doc() const;
154
155         typedef std::vector<detail::ArgumentInfoBase::ptr> Parameters;
156         Parameters parameters_;
157         std::string doc_;
158     };
159
160     /** \brief Parsed command overload
161
162         ParsedCommandOverload provides the command overload added to an OverloadedCommandNode for an
163         automatically parsed command.
164
165         This class is normally instantiated automatically when adding a function or member-function
166         pointer as callback to the tree. Manually instantiation this type of overload is \e not
167         simple, since the function signature has to be manipulated correctly to support the optional
168         \c std::ostream first argument.
169
170         \implementation This class is specialized for each supported number of command arguments.
171
172         \todo Implement automatic binding of member functions for parser and formatter
173      */
174     template <class FunctionTraits, class ReturnType=typename FunctionTraits::result_type, 
175               unsigned arity=FunctionTraits::arity>
176     class ParsedCommandOverload : public ParsedCommandOverloadBase
177     {
178     public:
179         typedef boost::intrusive_ptr<ParsedCommandOverload> ptr;
180
181 #ifdef DOXYGEN
182         static ptr create(Function fn);
183 #endif
184     };
185
186 #ifndef DOXYGEN
187
188 #   define BOOST_PP_ITERATION_PARAMS_1 (4, (0, SENF_CONSOLE_MAX_COMMAND_ARITY,                     \
189                                             SENF_ABSOLUTE_INCLUDE_PATH(Console/ParsedCommand.mpp), \
190                                             1))
191 #   include BOOST_PP_ITERATE()
192
193 #endif
194
195     /** \brief Generic ParsedCommandOverladBase attributes
196
197         Attributes for parsed commands are not set directly on the node. They are set via a special
198         attributor temporary returned when adding a parsed command to the tree.
199         
200         This class is the base class for those attributors. It provides members which do not depend
201         in any way on the exact type of command added.
202
203         \see \ref console_autoparse
204      */
205     class ParsedCommandAttributorBase
206     {
207     public:
208         OverloadedCommandNode & node() const; ///< Return the node object
209         operator OverloadedCommandNode & () const; ///< Automatically convert to node object
210
211     protected:
212         ParsedCommandAttributorBase(ParsedCommandOverloadBase & overload, unsigned index);
213
214         void argName(std::string const & name) const;
215         void argDoc(std::string const & doc) const;
216         template <class Type> void defaultValue(Type const & value) const;
217         void typeName(std::string const & doc) const;
218         void defaultDoc(std::string const & doc) const;
219
220         ParsedCommandOverloadBase & overload() const;
221         void overloadDoc(std::string const & doc) const;
222         void nodeDoc(std::string const & doc) const;
223         template <class Type, class Fn> void parser(Fn fn) const;
224         
225     private:
226         ParsedCommandOverloadBase & overload_;
227         unsigned index_;
228     };
229
230     /** \brief Non argument dependent ParsedCommandBase attributes 
231         
232         Attributes for parsed commands are not set directly on the node. They are set via a special
233         attributor temporary returned when adding a parsed command to the tree.
234
235         This class adds all those members, which do depend on the type of command added (and thereby
236         on that commands signature) but do not depend on the type of any single argument.
237
238         \see \ref console_autoparse
239      */
240     template <class Overload>
241     class ParsedCommandAttributor
242         : public ParsedCommandAttributorBase
243     {
244     public:
245         Overload & overload() const;    ///< Get the command overload
246
247     protected:
248         ParsedCommandAttributor(Overload & overload, unsigned index);
249
250     private:
251     };
252
253     /** \brief Keyword argument tags
254
255         The tags defined in this namespace are used as keyword arguments via the <a
256         href="http://www.boost.org/doc/libs/1_33_1/libs/parameter/doc/html/index.html">Boost.Parameter</a>
257         library.
258
259         For the keyword tags, the standard C++ scoping rules apply
260         \code
261         // Either qualify them with their complete namespace
262         dir.add(...)
263             .arg( senf::console::kw::name = "name" );
264         
265         // Or use a namespace alias
266         namespace kw = senf::console::kw;
267         dir.add(...)
268             .arg( kw::name = "name" );
269
270         // Or import the keywords into the current namespace (beware of name collisions)
271         using namespace senf::console::kw;
272         dir.add(...)
273             .arg( name = "name" );
274         \endcode
275
276         The second alternative is preferred, the <tt>using namespace</tt> directive may be used as
277         long as the keyword names do not clash with another visible symbol.
278
279         \section kw_attributes Argument attribute values
280
281         The keywords are used to set argument attributes.  The keywords \ref default_value and \ref
282         parser influence, how an argument is parsed/interpreted whereas \ref name, \ref description,
283         \ref type_name and \ref default_doc are used to change the arguments documentation:
284         \code
285         void command(int);
286
287         dir.add("command", &command)
288             .arg( kw::name          = "name",
289                   kw::description   = "description",
290                   kw::default_value = 1,
291                   kw::type_name     = "type_name",
292                   kw::default_doc   = "default_doc" );
293         \endcode
294         Will create the following documentation:
295         \htmlonly
296         <pre>
297         Usage:
298             command [name:type_name]
299         
300         With:
301             name      description
302                 default: default_doc
303         </pre>
304         \endhtmlonly
305
306         \see \ref senf::console::ParsedArgumentAttributor::arg()
307      */
308     namespace kw {
309         BOOST_PARAMETER_KEYWORD(type, name) ///< Argument name
310                                         /**< Sets the displayed name of the argument. */
311         BOOST_PARAMETER_KEYWORD(type, description) ///< One-line argument description
312                                         /**< This description is shown in the argument
313                                              reference. If several overloads have same-named
314                                              arguments, only one of them should be documented. This
315                                              documentation then applies to all arguments of that
316                                              name. */
317         BOOST_PARAMETER_KEYWORD(type, default_value) ///< Argument default value
318                                         /**< If a default value is specified for an argument, that
319                                              argument is optional. If an overload is called with
320                                              fewer arguments than defined, optional values will be
321                                              used beginning at the last optional argument and going
322                                              forward until all arguments have values assigned. E.g.,
323                                              an overload with 5 parameters \a a - \a e with two
324                                              defaults attached:
325                                              <pre>
326                                              command a:int [b:int] c:int [d:int] e:int
327                                              </pre>
328                                              When calling the overload, the arguments will be
329                                              assigned in the following way:
330                                              <table class="senf fixedwidth">
331                                              <tr>
332                                                <td><tt>command 1 2</tt></td>
333                                                <td>SyntaxErrorException: invalid number of
334                                                  arguments</td>
335                                              </tr>
336                                              <tr>
337                                                <td><tt>command 1 2 3</tt></td>
338                                                <td>\a a = 1, \n
339                                                    \a b = \e default, \n
340                                                    \a c = 2, \n
341                                                    \a d = \e default, \n
342                                                    \a e = 3</td>
343                                              </tr>
344                                              <tr>
345                                                <td><tt>command 1 2 3 4</tt></td>
346                                                <td>\a a = 1, \n
347                                                    \a b = 2, \n
348                                                    \a c = 3, \n
349                                                    \a d = \e default, \n
350                                                    \a e = 4</td>
351                                              </tr>
352                                              <tr>
353                                                <td><tt>command 1 2 3 4 5</tt></td>
354                                                <td>\a a = 1, \n
355                                                    \a b = 2, \n
356                                                    \a c = 3, \n
357                                                    \a d = 4, \n
358                                                    \a e = 5</td>
359                                              </tr>
360                                              <tr>
361                                                <td><tt>command 1 2 3 4 5 6</tt></td>
362                                                <td>SyntaxErrorException: invalid number of
363                                                  arguments</td>
364                                              </tr>
365                                              </table>
366                                              So, if you assign default values as you are used to
367                                              they will work like in C++ and most other languages */
368         BOOST_PARAMETER_KEYWORD(type, type_name) ///< Type name of this arguments type
369                                         /**< By default, the type of an argument is extracted from
370                                              the C++ type name by taking the last component of the
371                                              fully scoped name. This value can be changed by setting
372                                              this attribute. */
373         BOOST_PARAMETER_KEYWORD(type, default_doc) ///< String rep of default value
374                                         /**< By default, the default value is documented by
375                                              converting the value to it's string representation
376                                              using \c boost::lexical_cast / \c iostreams. The
377                                              displayed value can be changed by setting this
378                                              attribute. */
379         BOOST_PARAMETER_KEYWORD(type, parser) ///< Argument parser
380                                         /**< The argument parser is used to convert the argument
381                                              token list returned by the console/config parser into
382                                              the appropriate value. If not set explicitly, this
383                                              conversion is supplied by the ArgumentTraits
384                                              class. 
385
386                                              Setting the \a parser attribute allows to use a custom
387                                              parser. The parser is an arbitrary callable object with
388                                              the signature
389                                              \code
390                                              void parser(senf::console::ParseCommandInfo::TokensRange const & tokens, value_type & out);
391                                              \endcode
392                                              where \c value_type is the type of the overload
393                                              parameter. The parser must read and parse the complete
394                                              \a tokens range and return the parsed value in \a
395                                              out. If the parser fails, it must raise a
396                                              senf::console::SyntaxErrorException. */
397     }
398
399     /** \brief Derived class dependent ParsedCommandBase attributes
400
401         Attributes for parsed commands are not set directly on the node. They are set via a special
402         attributor temporary returned when adding a parsed command to the tree.
403         
404         This class adds all those members, which do not depend on any specific argument but which
405         need to return the correct attributor type.
406
407         \see \ref console_autoparse
408      */
409     template <class Overload, class Self, class ReturnType=typename Overload::traits::result_type>
410     class ParsedArgumentAttributorBase
411         : public ParsedCommandAttributor<Overload>
412     {
413     public:
414         Self doc(std::string const & doc) const; ///< Set documentation for all overloads
415         Self overloadDoc(std::string const & doc) const; ///< Set overload specific documentation
416         Self formatter(typename Overload::Formatter f) const; ///< Set return value formatter
417
418     protected:
419         ParsedArgumentAttributorBase(Overload & overload, unsigned index);
420
421     private:
422     };
423
424 #ifndef DOXYGEN
425
426     template <class Overload, class Self>
427     class ParsedArgumentAttributorBase<Overload, Self, void>
428         : public ParsedCommandAttributor<Overload>
429     {
430     public:
431         Self doc(std::string const & doc) const; ///< Set documentation for all overloads
432         Self overloadDoc(std::string const & doc) const; ///< Set overload specific documentation
433
434     protected:
435         ParsedArgumentAttributorBase(Overload & overload, unsigned index);
436
437     private:
438     };
439
440 #endif
441     
442     /** \brief Argument dependent ParsedCommandBase attributes
443
444         Attributes for parsed commands are not set directly on the node. They are set via a special
445         attributor temporary returned when adding a parsed command to the tree.
446         
447         This class adds all those members, which depend on a specific argument. Each call to \c arg
448         will advance to the next argument.
449
450         \see \ref console_autoparse
451      */
452     template < class Overload, 
453                unsigned index=0, 
454                bool flag=(index < unsigned(Overload::traits::arity)) >
455     class ParsedArgumentAttributor
456         : public ParsedArgumentAttributorBase< Overload, 
457                                                 ParsedArgumentAttributor<Overload, index, flag> >
458     {
459         typedef boost::parameter::parameters<
460             kw::type::name,
461             kw::type::description,
462             kw::type::default_value,
463             kw::type::type_name,
464             kw::type::default_doc,
465             kw::type::parser> arg_params;
466
467     public:
468         typedef OverloadedCommandNode node_type;
469         typedef ParsedArgumentAttributor return_type;
470
471         typedef typename senf::function_traits_arg_type< 
472             typename Overload::traits, int(index) >::type arg_type;
473         typedef typename senf::remove_cvref< arg_type >::type value_type;
474         typedef ParsedArgumentAttributor<Overload, index+1> next_type;
475
476         next_type arg() const;          ///< Set argument attributes
477                                         /**< This member changes the attributes for the current
478                                              argument. The attributes are passed to arg() as keyword
479                                              arguments using the <a
480                                              href="http://www.boost.org/doc/libs/1_33_1/libs/parameter/doc/html/index.html">Boost.Parameter</a>
481                                              library. 
482                                              \code
483                                              ...
484                                                  .arg( kw::name          = "name", 
485                                                        kw::default_value = 1 )
486                                              ...
487                                              \endcode
488                                              The valid keywords are defined in the senf::console::kw
489                                              namespace.
490                                              
491                                              Each call to arg() will increment the argument index
492                                              and advance to the next argument. This member is only
493                                              present, if there is an argument at the current. */
494
495 #ifndef DOXYVEN
496
497 #       define BOOST_PP_ITERATION_PARAMS_1                                                        \
498             (4, (1, BOOST_PARAMETER_MAX_ARITY,                                                    \
499                  SENF_ABSOLUTE_INCLUDE_PATH(Console/ParsedCommand.mpp),                           \
500                  5))
501 #       include BOOST_PP_ITERATE()
502
503 #endif
504
505     private:
506         explicit ParsedArgumentAttributor(Overload & overload);
507
508         template <class ArgumentPack>
509         next_type argInfo(ArgumentPack const & args) const;
510         template <class Kw, class ArgumentPack>
511         void argInfo(Kw const &, ArgumentPack const &, boost::mpl::false_) 
512             const;
513
514         template <class ArgumentPack>
515         void argInfo(boost::parameter::keyword<kw::type::name> const &, 
516                      ArgumentPack const & args, boost::mpl::true_) 
517             const;
518         template <class ArgumentPack>
519         void argInfo(boost::parameter::keyword<kw::type::description> const &, 
520                      ArgumentPack const & args, boost::mpl::true_) 
521             const;
522         template <class ArgumentPack>
523         void argInfo(boost::parameter::keyword<kw::type::default_value> const &, 
524                      ArgumentPack const & args, boost::mpl::true_) 
525             const;
526         template <class ArgumentPack>
527         void argInfo(boost::parameter::keyword<kw::type::type_name> const &, 
528                      ArgumentPack const & args, boost::mpl::true_) 
529             const;
530         template <class ArgumentPack>
531         void argInfo(boost::parameter::keyword<kw::type::default_doc> const &, 
532                      ArgumentPack const & args, boost::mpl::true_) 
533             const;
534         template <class ArgumentPack>
535         void argInfo(boost::parameter::keyword<kw::type::parser> const &, 
536                      ArgumentPack const & args, boost::mpl::true_) 
537             const;
538
539         next_type next() const;
540
541         void defaultValue(value_type const & value) const;
542         template <class Fn> void parser(Fn fn) const;
543
544         template <class O, unsigned i, bool f>
545         friend class ParsedArgumentAttributor;
546
547 #ifndef DOXYGEN
548         
549         template <class Function>
550         friend ParsedArgumentAttributor<
551             ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
552         senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int);
553         
554         template <class Owner, class Function>
555         friend ParsedArgumentAttributor<
556             ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
557         senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
558                               Function fn, int,
559                               typename boost::enable_if_c<
560                                   detail::ParsedCommandTraits<Function>::is_member>::type * = 0);
561
562 #endif
563     };
564
565 #ifndef DOXYGEN
566
567     template <class Overload, unsigned index>
568     class ParsedArgumentAttributor<Overload, index, false>
569         : public ParsedArgumentAttributorBase< Overload, 
570                                                 ParsedArgumentAttributor<Overload, index, false> >
571     {
572     public:
573         typedef OverloadedCommandNode node_type;
574         typedef ParsedArgumentAttributor return_type;
575
576     private:
577         explicit ParsedArgumentAttributor(Overload & overload);
578
579         template <class O, unsigned i, bool f>
580         friend class ParsedArgumentAttributor;
581         
582         template <class Function>
583         friend ParsedArgumentAttributor<
584             ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
585         senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int);
586         
587         template <class Owner, class Function>
588         friend ParsedArgumentAttributor<
589             ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
590         senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
591                               Function fn, int,
592                               typename boost::enable_if_c<
593                                   detail::ParsedCommandTraits<Function>::is_member>::type * = 0);
594     };
595
596     template <class Function>
597     ParsedArgumentAttributor<
598         ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
599     senf_console_add_node(DirectoryNode & node, std::string const & name, Function fn, int);
600
601     template <class Owner, class Function>
602     ParsedArgumentAttributor<
603         ParsedCommandOverload<typename detail::ParsedCommandTraits<Function>::traits> >
604     senf_console_add_node(DirectoryNode & node, Owner & owner, std::string const & name,
605                           Function fn, int,
606                           typename boost::enable_if_c<
607                               detail::ParsedCommandTraits<Function>::is_member>::type * = 0);
608
609 #endif
610
611 }}
612
613 #include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
614
615 BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedCommandOverload, (class,unsigned))
616 BOOST_TYPEOF_REGISTER_TEMPLATE(senf::console::ParsedArgumentAttributor, (class, unsigned, bool))
617 BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function_traits, 1)
618
619 ///////////////////////////////hh.e////////////////////////////////////////
620 #include "ParsedCommand.cci"
621 #include "ParsedCommand.ct"
622 #include "ParsedCommand.cti"
623 #endif
624
625 \f
626 // Local Variables:
627 // mode: c++
628 // fill-column: 100
629 // comment-column: 40
630 // c-file-style: "senf"
631 // indent-tabs-mode: nil
632 // ispell-local-dictionary: "american"
633 // compile-command: "scons -u test"
634 // End: