From: g0dil Date: Mon, 21 Apr 2008 10:41:13 +0000 (+0000) Subject: Console: Registration of member enums X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=9680ed9010b85a635d3a9131710bfed26e4c165c;p=senf.git Console: Registration of member enums Console: Enum and custom parser documentation git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@820 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Console/Mainpage.dox b/Console/Mainpage.dox index 2466616..1d09056 100644 --- a/Console/Mainpage.dox +++ b/Console/Mainpage.dox @@ -108,7 +108,7 @@ \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 @@ -203,7 +203,7 @@ 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 @@ -303,7 +303,7 @@ Commands may have an optional first argument of type std::ostream &. 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) @@ -700,7 +700,7 @@ \link senf::console::VariableAttributor::doc() .doc\endlink ( \e doc )Set variable documentation - \link senf::console::VariableAttributor::onChange() .onchange\endlink + \link senf::console::VariableAttributor::onChange() .onChange\endlink ( \e handler )Set change handler @@ -729,7 +729,146 @@ 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 +
+    server:/$ test9 Sit
+    Sit
+    server:/$ test9 Crawl
+    argument syntax error: invalid enum value
+    server:/$ help test9
+    Usage:
+        test9 arg11:MyEnum
+    server:/$
+    
+ \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 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 +
+    server:/$ test3ob/test Red
+    Red
+    server:/$ test3ob/test White
+    argument syntax error: invalid enum value
+    server:/$ help test3ob/test
+    Usage:
+        test arg11:Color
+    
+ \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::parse( + senf::console::ParseCommandInfo::TokensRange( tokens.begin(), tokens.begin()+1 ), + out.x ) + senf::console::ArgumentTraits::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 +
+    server:/$ test10 (2 7)
+    (4 14)
+    server:/$ help test10
+    Usage:
+        test10 [x:Coordinate]
+
+    With:
+        x         Coordinate to double
+            default: (0 0)
+    server:/$
+    
+ \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. */ diff --git a/Console/Traits.cc b/Console/Traits.cc index 9207a13..81acbe7 100644 --- a/Console/Traits.cc +++ b/Console/Traits.cc @@ -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; } diff --git a/Console/Traits.hh b/Console/Traits.hh index 779ef30..4346eb0 100644 --- a/Console/Traits.hh +++ b/Console/Traits.hh @@ -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 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 struct ArgumentTraits @@ -99,10 +114,47 @@ namespace console { ReturnValueTraits for this conversion. */ }; + /** \brief Argument parser + + \see ArgumentTraits + + \related ArgumentTraits + */ template 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) }} diff --git a/Console/Traits.ih b/Console/Traits.ih index 12fbb65..2e46835 100644 --- a/Console/Traits.ih +++ b/Console/Traits.ih @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "../Utils/singleton.hh" @@ -40,6 +41,8 @@ namespace senf { namespace console { namespace detail { +#ifndef DOXYGEN + typedef boost::bimap 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(e)) +# define SENF_CONSOLE_REGISTER_ENUM_ELT(r,d,e) \ + (BOOST_PP_STRINGIZE(e), static_cast(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 & traits ( \ - senf::console::detail::EnumTraits::instance() ); \ + senf::console::detail::EnumTraits & traits ( \ + senf::console::detail::EnumTraits::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( \ + senf_console_init_enum_table( Prefix Type() ); \ + out = static_cast( \ senf::console::detail::parseEnum( \ - senf::console::detail::EnumTraits::instance().table, tokens)); \ + senf::console::detail::EnumTraits::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::instance().table, \ + senf::console::detail::EnumTraits::instance().table, \ static_cast(value) ); \ } +#endif + }}} ///////////////////////////////ih.e//////////////////////////////////////// diff --git a/Console/Traits.test.cc b/Console/Traits.test.cc index 861d0f0..b11c4b5 100644 --- a/Console/Traits.test.cc +++ b/Console/Traits.test.cc @@ -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( 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( 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( boost::ref(executor), boost::ref(ss), _1 )) ); + BOOST_CHECK_EQUAL( ss.str(), "MemberBar\n" ); } diff --git a/senf.dict b/senf.dict index d853092..f02a484 100644 --- 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