Console: Registration of member enums
g0dil [Mon, 21 Apr 2008 10:41:13 +0000 (10:41 +0000)]
Console: Enum and custom parser documentation

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@820 270642c3-0616-0410-b53a-bc976706d245

Console/Mainpage.dox
Console/Traits.cc
Console/Traits.hh
Console/Traits.ih
Console/Traits.test.cc
senf.dict

index 2466616..1d09056 100644 (file)
     \see \ref node_tree
 
 
-    \section intro_commands Console/config commands
+    \section intro_commands Registering console/config commands
 
     The console/config language does not define, how arguments are passed to the commands, it just
     tokenizes the input and passes the tokens to the commands which then handle the
         if (command.arguments().size() != 1) 
             raise senf::console::SyntaxErrorException("invalid number of arguments");
 
-        senf::console::ParseCommandInfo::TokenRange & argTokens (
+        senf::console::ParseCommandInfo::TokensRange & argTokens (
             command.arguments()[0]);
 
         // The argument must have exactly one token
 
     Commands may have an optional first argument of type <tt>std::ostream &</tt>. This argument is
     not considered part of the real interface. When the command is executed, the callback will be
-    passed the current console's output stream object in this argument. With this, the callback can
+    passed the current consoles output stream object in this argument. With this, the callback can
     output arbitrary messages to the network console.
     \code
     void fun3(std::ostream & os, unsigned n, std::string text)
     <tr><td style="width:14em">\link senf::console::VariableAttributor::doc() .doc\endlink
     ( \e doc )</td><td>Set variable documentation</td></tr>
     
-    <tr><td>\link senf::console::VariableAttributor::onChange() .onchange\endlink
+    <tr><td>\link senf::console::VariableAttributor::onChange() .onChange\endlink
     ( \e handler )</td><td>Set change handler</td></tr>
     
     </table>
     After this setup, \c varChanged will be called, whenever the value has changed.
 
 
-    \see senf::console::VariableAttributor for the complete attribute interface
+    \section console_args Registering special argument types
+
+    By default, argument types which can be read and written using \c iostreams are automatically
+    supported. Other types need to be registered explicitly
+
+
+    \subsection console_args_enum Registering enum types
+
+    Enum types are a special case, since it is not possible, to find a string representation for the
+    enumerator values automatically. Therefore, enum types need to be registered manually.
+    \code
+    enum MyEnum { Sit, Run, Jump };
+    SENF_CONSOLE_REGISTER_ENUM( MyEnum, (Sit)(Run)(Jump) );
+
+    MyEnum fun4(MyEnum v) { return v }
+
+    senf::console::root()
+        .add("test9", &fun4);
+    \endcode
+
+    After an enum type is registered, it can be used like any other type for arguments or
+    return-values:
+
+    \htmlonly
+    <pre>
+    server:/$ test9 Sit
+    Sit
+    server:/$ test9 Crawl
+    argument syntax error: invalid enum value
+    server:/$ help test9
+    Usage:
+        test9 arg11:MyEnum
+    server:/$
+    </pre>
+    \endhtmlonly
+
+    \ref SENF_CONSOLE_REGISTER_ENUM() can only be used, to register enums at namespace scope. To
+    register enums defined within some class, use \ref SENF_CONSOLE_REGISTER_ENUM_MEMBER()
+
+    \code
+    class Test3
+    {
+    public:
+        enum Color { Red, Green, Blue };
+    
+        senf::console::ScopedDirectory<MyClass> dir;
+
+        Test3();
+
+        Color mem3(Color c) { return c }
+    };
+    SENF_CONSOLE_REGISTER_ENUM_MEMBER( Test3, Color, (Red)(Green)(Blue) );
+
+    Test3::Test3() : dir(this)
+        { dir.add("test", &MyClass::mem3); }
+    
+    Test3 test3ob;
+    senf::console::root().add("test3ob", test3ob.dir);
+    \endcode
+
+    Using this command/type is identical
+    \htmlonly
+    <pre>
+    server:/$ test3ob/test Red
+    Red
+    server:/$ test3ob/test White
+    argument syntax error: invalid enum value
+    server:/$ help test3ob/test
+    Usage:
+        test arg11:Color
+    </pre>
+    \endhtmlonly
+
+
+    \subsection console_args_custom Customizing argument and return value parsing/formatting
+
+    To support or customize parsing/formatting of other types, they need to be registered. In it's
+    simplest case, this works, by just providing an appropriate overload for
+    senf_console_parse_argument() and senf_console_format_value():
+    \code
+    struct Coordinate
+    {
+        Coordinate() : x(0), y(0) {}
+        Coordinate(int x_, int y_) : x(x_), y(y_) {}
+
+        int x, y;
+    }
+
+    void senf_console_parse_argument(senf::console::ParseCommandInfo::TokensRange const & tokens,
+                                     MyClass & out)
+    {
+        if (tokens.size() != 2)
+            throw SyntaxErrorException("parameter syntax error");
+        senf::console::ArgumentTraits<int>::parse(
+            senf::console::ParseCommandInfo::TokensRange( tokens.begin(), tokens.begin()+1 ),
+            out.x )
+        senf::console::ArgumentTraits<int>::parse(
+            senf::console::ParseCommandInfo::TokensRange( tokens.begin()+1, tokens.end() ),
+            out.y )
+    }
+
+    void senf_console_format_value(Coordinate const & value, std::ostream & os)
+    {
+        os << '(' << value.x << ' ' << value.y << ')';
+    }
+    \endcode
+    The parser will accept an argument with two tokens which are each forwarded to the integer
+    parser. The formatter writes out the value as a parenthesized pair.
+
+    \code
+    Coordinate fun5(Coordinate const & p) { return Coordinate(2*p.x, 2*p.y) }
+    
+    namespace kw = senf::console::kw;
+
+    senf::console::root()
+        .add("test10", &fun5)
+        .arg("x","coordinate to double",
+             kw::default_value = Coordinate())
+    \endcode
+    We can now call \c test10 with a coordinate argument:
+    \htmlonly
+    <pre>
+    server:/$ test10 (2 7)
+    (4 14)
+    server:/$ help test10
+    Usage:
+        test10 [x:Coordinate]
+
+    With:
+        x         Coordinate to double
+            default: (0 0)
+    server:/$
+    </pre>
+    \endhtmlonly
+    
+    If you want to customize the formatting of default values differently from the formating of
+    return-values or if you want to change the displayed name of a type, you will need to specialize
+    the senf::console::ArgumentTraits class instead of implementing
+    senf_console_parse_argument(). See senf::console::ArgumentTraits and
+    senf::console::ReturnValueTraits for more.
  */
 
 \f
index 9207a13..81acbe7 100644 (file)
@@ -41,7 +41,7 @@ prefix_ long senf::console::detail::parseEnum(EnumTable const & table,
 
     EnumTable::left_map::const_iterator i (table.left.find(tokens.begin()[0].value()));
     if (i == table.left.end())
-        throw SyntaxErrorException("parameter syntax error: Invalid enum value");
+        throw SyntaxErrorException("parameter syntax error: invalid enum value");
     return i->second;
 }
 
index 779ef30..4346eb0 100644 (file)
@@ -43,9 +43,12 @@ namespace console {
     /** \brief Customize return value formating
 
         ReturnValueTraits provides return value formatting. The default implementation provided here
-        will just write the value to the output stream. 
+        will forward the call directly to senf_console_format_value(). The default implementation of
+        that function will write the \a value to \a os using standard iostream formatting.
 
-        To customize this behavior for some type, specialize this class for the type.
+        To customize this behavior for some type, either provide an implementation of
+        senf_console_format_value() in the types namespace or provide a specialization of
+        ReturnValueTraits.
 
         The output should \e not end in a newline since one is added automatically.
      */
@@ -57,7 +60,13 @@ namespace console {
         static void format(Type const & value, std::ostream & os);
                                         ///< Write \a value to \a os
     };
-    
+
+    /** \brief Return value formatter
+
+        \see ReturnValuetraits
+
+        \related ReturnValueTraits
+     */
     template <class Type>
     void senf_console_format_value(Type const & value, std::ostream & os);
 
@@ -67,12 +76,18 @@ namespace console {
         string-description of a type and to convert a value back into it's string representation
         used to display default values.
         
-        The default implementation provided here will use \c boost::lexical_cast and thereby \c
-        iostreams to convert an argument consisting of a single input token into the required
-        type. Types are named by returning the last component of the fully scoped name (e.g. \c
-        "string" for \c std::string). Values are formatted again using \c boost::lexical_cast.
-
-        To customize this behavior for some type, specialize this class for the type.
+        The default implementation provided here 
+        \li will use senf_console_parse_argument() to parse a value. This functions default
+            implementation uses \c boost::lexical_cast and thereby \c iostreams to convert an
+            argument consisting of a single input token into the required type.
+        \li will name types by returning the last component of the fully scoped name (e.g. \c
+            "string" for \c std::string). 
+        \li Will format values (for default value display) by forwarding the value to the
+            ReturnValueTraits of that type.
+
+        To customize just the argument parsing, just provide an implementation of
+        senf_console_parse_argument(). Alternatively or to customize type naming or default value
+        formatting, specialize ArgumentTraits  for the type.
      */
     template <class Type>
     struct ArgumentTraits
@@ -99,10 +114,47 @@ namespace console {
                                              ReturnValueTraits for this conversion. */
     };
 
+    /** \brief Argument parser
+
+        \see ArgumentTraits
+
+        \related ArgumentTraits
+     */
     template <class Type>
     void senf_console_parse_argument(ParseCommandInfo::TokensRange const & tokens, Type & out);
 
-#   define SENF_CONSOLE_REGISTER_ENUM(Type, Values) SENF_CONSOLE_REGISTER_ENUM_(Type, Values)
+    /** \brief Register enum type for argument parsing
+
+        Enum types need to be registered explicitly to support parsing. 
+        \code
+        enum Foo { Foo1, Foo2 };
+        SENF_CONSOLE_REGISTER_ENUM( Foo, (Foo1)(Foo2) );
+        \endcode
+        This macro will register an enum type and it's enumerators defined at namespace scope. See
+        \ref SENF_CONSOLE_REGISTER_ENUM_MEMBER to register a member enum type.
+
+        \ingroup console_commands
+     */
+#   define SENF_CONSOLE_REGISTER_ENUM(Type, Values) \
+        SENF_CONSOLE_REGISTER_ENUM_(BOOST_PP_EMPTY(), Type, Values)
+
+    /** \brief Register enum type for argument parsing
+
+        Enum types need to be registered explicitly to support parsing. 
+        \code
+        class SomeClass
+        {
+            enum Foo { Foo1, Foo2 };
+        };
+
+        SENF_CONSOLE_REGISTER_ENUM_MEMBER( SomeClass, Foo, (Foo1)(Foo2) );
+        \endcode This macro will register an enum type and it's enumerators defined in a class. See
+        \ref SENF_CONSOLE_REGISTER_ENUM to register an enum type declared at namespace scope.
+
+        \ingroup console_commands
+     */
+#   define SENF_CONSOLE_REGISTER_ENUM_MEMBER(Class, Type, Values) \
+        SENF_CONSOLE_REGISTER_ENUM_(Class::, Type, Values)
 
 }}
 
index 12fbb65..2e46835 100644 (file)
@@ -30,6 +30,7 @@
 #include <string>
 #include <boost/preprocessor/seq/for_each.hpp>
 #include <boost/preprocessor/stringize.hpp>
+#include <boost/preprocessor/facilities/empty.hpp>
 #include <boost/bimap.hpp>
 #include <boost/assign/list_inserter.hpp>
 #include "../Utils/singleton.hh"
@@ -40,6 +41,8 @@ namespace senf {
 namespace console {
 namespace detail {
 
+#ifndef DOXYGEN
+
     typedef boost::bimap<std::string, long> EnumTable;
 
     long parseEnum(EnumTable const & table, ParseCommandInfo::TokensRange const & tokens);
@@ -52,33 +55,36 @@ namespace detail {
         EnumTable table;
     };
 
-#   define SENF_CONSOLE_REGISTER_ENUM_ELT(r,d,e) (BOOST_PP_STRINGIZE(e), static_cast<long>(e))
+#   define SENF_CONSOLE_REGISTER_ENUM_ELT(r,d,e) \
+        (BOOST_PP_STRINGIZE(e), static_cast<long>(d e))
 
-#   define SENF_CONSOLE_REGISTER_ENUM_(Type, Values)                                              \
-        void senf_console_init_enum_table(Type)                                                   \
+#   define SENF_CONSOLE_REGISTER_ENUM_(Prefix, Type, Values)                                      \
+        void senf_console_init_enum_table(Prefix Type)                                            \
         {                                                                                         \
-            senf::console::detail::EnumTraits<Type> & traits (                                    \
-                senf::console::detail::EnumTraits<Type>::instance() );                            \
+            senf::console::detail::EnumTraits<Prefix Type> & traits (                             \
+                senf::console::detail::EnumTraits<Prefix Type>::instance() );                     \
             if (traits.table.empty())                                                             \
                 boost::assign::insert(traits.table)                                               \
-                    BOOST_PP_SEQ_FOR_EACH( SENF_CONSOLE_REGISTER_ENUM_ELT, _, Values );           \
+                    BOOST_PP_SEQ_FOR_EACH( SENF_CONSOLE_REGISTER_ENUM_ELT, Prefix, Values );      \
         }                                                                                         \
         void senf_console_parse_argument(                                                         \
-            senf::console::ParseCommandInfo::TokensRange const & tokens, Type & out)              \
+            senf::console::ParseCommandInfo::TokensRange const & tokens, Prefix Type & out)       \
         {                                                                                         \
-            senf_console_init_enum_table( Type() );                                               \
-            out = static_cast<Type>(                                                              \
+            senf_console_init_enum_table( Prefix Type() );                                        \
+            out = static_cast<Prefix Type>(                                                       \
                 senf::console::detail::parseEnum(                                                 \
-                    senf::console::detail::EnumTraits<Type>::instance().table, tokens));          \
+                    senf::console::detail::EnumTraits<Prefix Type>::instance().table, tokens));   \
         }                                                                                         \
-        void senf_console_format_value(Type value, std::ostream & os)                             \
+        void senf_console_format_value(Prefix Type value, std::ostream & os)                      \
         {                                                                                         \
-            senf_console_init_enum_table( Type() );                                               \
+            senf_console_init_enum_table( Prefix Type() );                                        \
             os << senf::console::detail::formatEnum(                                              \
-                senf::console::detail::EnumTraits<Type>::instance().table,                        \
+                senf::console::detail::EnumTraits<Prefix Type>::instance().table,                 \
                 static_cast<long>(value) );                                                       \
         }
 
+#endif
+
 }}}
 
 ///////////////////////////////ih.e////////////////////////////////////////
index 861d0f0..b11c4b5 100644 (file)
@@ -44,6 +44,13 @@ namespace {
     SENF_CONSOLE_REGISTER_ENUM( TestEnum, (Foo)(Bar) );
 
     TestEnum test (TestEnum value) { return value; }
+
+    struct TestClass {
+        enum MemberEnum { MemberFoo, MemberBar };
+        static MemberEnum test (MemberEnum value) { return value; }
+    };
+    SENF_CONSOLE_REGISTER_ENUM_MEMBER( TestClass, MemberEnum, (MemberFoo)(MemberBar) );
+    
 }
 
 BOOST_AUTO_UNIT_TEST(enumSupport)
@@ -76,6 +83,20 @@ BOOST_AUTO_UNIT_TEST(enumSupport)
         parser.parse("test/test Baz",
                      boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )),
         senf::console::SyntaxErrorException );
+
+    dir.add("member", &TestClass::test);
+
+    ss.str("");
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/member MemberFoo",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "MemberFoo\n" );
+
+    ss.str("");
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/member MemberBar",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "MemberBar\n" );
 }
 
 
index d853092..f02a484 100644 (file)
--- a/senf.dict
+++ b/senf.dict
@@ -19,12 +19,17 @@ alloc
 anotherCallback
 api
 arg
+argattributes
 argc
 args
+argTokens
 ArgumentToken
 argv
 async
+attr
 Augustin
+autoadd
+autoparse
 autoThrottling
 autotoc
 aVectorCollection
@@ -35,8 +40,10 @@ berlios
 BerliOS
 bitfield
 bool
+boostfn
 bund
 calculateChecksum
+callables
 callback
 callbacks
 CapitalziedLettersToSeparateWords
@@ -50,9 +57,11 @@ cfi
 checksumEnabled
 checksumPresent
 CIDR
+classsenf
 ClientSocketHandle
 CloneSource
 cmd
+cmdadd
 com
 CommandNode
 CommandOverload
@@ -70,6 +79,7 @@ CPPDEFINES
 CPPPATH
 createAfter
 createBefore
+cref
 ct
 cti
 CXXFLAGS
@@ -103,11 +113,13 @@ dt
 ElementParser
 enableChecksum
 endcode
+endhtmlonly
 endif
 endl
 endlink
 ENOFILE
 enum
+enums
 env
 eof
 EPIPE
@@ -148,6 +160,7 @@ FileTarget
 findNext
 findPrev
 fixedcolumn
+fixedwidth
 fixme
 fixvariant
 flurgle
@@ -188,6 +201,7 @@ hpp
 href
 htm
 html
+htmlonly
 http
 iana
 ias
@@ -227,11 +241,13 @@ ipv
 IPv
 IPX
 isock
+iter
 iterator
 jens
 jkaeber
 join
 key
+kw
 li
 libboost
 libc
@@ -255,14 +271,19 @@ mac
 MACAddress
 MACAddressParser
 mainpage
+manualparse
+mem
+memberfn
 mixin
 mkdir
 MPEGDVBBundle
 mpp
 multicast
+MyClass
 mycommand
 mydir
 myDirectory
+MyEnum
 MyList
 MyNodeType
 MyParser
@@ -293,7 +314,8 @@ nUsing
 ob
 ObjectDirectory
 offene
-Ok
+ok
+onChange
 onRequest
 onThrottle
 onUnthrottle
@@ -310,6 +332,7 @@ OutputConnector
 outputRequest
 outputSocket
 OverlayField
+overloadDoc
 OverloadedCommand
 OverloadedCommandNode
 PacketData
@@ -332,6 +355,8 @@ param
 ParseArray
 parsecollection
 ParseCommandInfo
+ParsedArgumentAttributor
+ParsedArgumentAttributorBase
 ParseHelpers
 parseint
 ParseInt
@@ -443,6 +468,7 @@ SIGVTALRM
 SIGWINCH
 SIGXCPU
 SIGXFSZ
+SimpleCommand
 SimpleCommandNode
 SimpleCommandOverload
 SimplePacketType
@@ -493,6 +519,8 @@ ThresholdQueueing
 ThrottleBarrier
 tigris
 todo
+tokenizes
+TokensRange
 tr
 TruncatedPacketException
 tt
@@ -518,6 +546,12 @@ unthrottles
 unthrottling
 Utils
 var
+varadd
+varattr
+varchange
+varChanged
+VariableAttributor
+varro
 VectorN
 Ver
 vlanId