From: g0dil Date: Thu, 17 Apr 2008 15:37:52 +0000 (+0000) Subject: Console: Add 'Variable' command 'onChange' implementation X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=a99eba1e1f51afcd7867344f50206f4975cfd5dd;p=senf.git Console: Add 'Variable' command 'onChange' implementation Console: 'Variable' command documentation git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@818 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Console/Mainpage.dox b/Console/Mainpage.dox index 62cffd6..2466616 100644 --- a/Console/Mainpage.dox +++ b/Console/Mainpage.dox @@ -358,6 +358,21 @@ server:/$ + One note: When taking the address of an overloaded function (member or non-member), the C++ + language forces you to cast that address to one of the possible types so the compiler knows, + which overload is requested. So to add a function which is overloaded in C++, each overload + needs to be added explicitly, casting to the correct type: + \code + void over(int); + void over(int,int); + + senf::console::root() + .add("over", static_cast(&over)); + senf::console::root() + .add("over", static_cast(&over)); + \endcode + + \subsection console_attributes Attributes As have seen so far, some documentation is automatically provided. We can add more info, by @@ -583,21 +598,19 @@ Member functions are supported like non-member functions. They must however be added through a senf::console::ScopedDirectory instance to bind them to their instance. \code - class Test + class Test1 { public: - senf::console::ScopedDirectory dir; - - Test(std::string label) : dir(this), label_ (label) - { - dir.add("test4", &Test::test2); - dir.add("test4", &Test::test3); - } + senf::console::ScopedDirectory dir; - std::string test2(std::string const & text) + Test1(std::string label) : dir(this), label_ (label) + { dir.add("test", &Test::test1); + dir.add("test", &Test::test2); } + + std::string test1(std::string const & text) { return label_ + ": " + text; } - void test3(std::ostream & os, unsigned n, std::string const & text) + void test2(std::ostream & os, unsigned n, std::string const & text) { while (n-- > 0) os << label << ": " << text << std::endl; } private: @@ -606,12 +619,117 @@ // ... - Test testOb ("test"); - senf::console::root().add("testobj", testOb.dir); + Test1 test1ob ("test"); + senf::console::root().add("test1ob", test1ob.dir); \endcode Binding via senf::console::ScopedDirectory ensures, that the commands are automatically removed from the tree when the object is destroyed. + + + \section console_variables Variables + + \subsection console_varadd Adding + + The console/config library supports the direct registration of variables as commands. A + variable command consists of two overloads, one to query the current value and one to change the + value. + \code + class Test2 + { + public: + senf::console::ScopedDirectory dir; + + Test2() : dir(this), var_(0) + { dir.add("var", var_); } + + private: + int var_; + }; + + Test2 test2ob; + senf::console::root().add("test2ob", test2ob.dir); + \endcode + This shows the most common scenario: A member variable is added to a ScopedDirectory of the same + class. This ensures, that the variable command node is removed from the tree when the instance + (and thereby the variable) are destroyed. The variable can now be used like any other command: + \htmlonly +
+    server:/$ test2ob/var
+    0
+    server:/$ test2ob/var 10
+    server:/$ test2ob/var
+    10
+    server:/$ help test2ob
+    Usage:
+        1- var new_value:int
+        2- var
+    server:/$
+    
+ \endhtmlonly + + + \subsection console_varro Read-only variables + + The library also supports read-only variables. To make a variable read-only, just wrap it in \c + boost::cref() (where \c cref stands for \c const reference) + \code + int var (0); + + senf::console::root().add("var1", boost::cref(var)); + \endcode + A read-only variable only has a single overload: + \htmlonly +
+    server:/$ var1
+    0
+    server:/$ help var1
+    Usage:
+        var1
+    server:/$ 
+    
+ \endhtmlonly + + + \subsection console_varattr Attributes + + The most important Variable command attributes are + + + + + + + +
\link senf::console::VariableAttributor::doc() .doc\endlink + ( \e doc )Set variable documentation
\link senf::console::VariableAttributor::onChange() .onchange\endlink + ( \e handler )Set change handler
+ + \see senf::console::VariableAttributor for the complete attribute interface + + \subsection console_varchange Change notification + + A \e handler can be set to be called, whenever the variable is changed. It will be called with a + reference to the old value. The handler is called, after the value has been changed + + \code + int var (0); + + // Since this is int, it would make sense to declare the argument pass-by-value (int old) + // but for more complex args, use a const & here + void varChanged(int const & old) + { + // ... + } + + senf::console::root().add("var2",var) + .onChange(&varChanged); + \endcode + + After this setup, \c varChanged will be called, whenever the value has changed. + + + \see senf::console::VariableAttributor for the complete attribute interface */ diff --git a/Console/ParsedCommand.cti b/Console/ParsedCommand.cti index 64e8100..0ba97d2 100644 --- a/Console/ParsedCommand.cti +++ b/Console/ParsedCommand.cti @@ -310,6 +310,8 @@ namespace senf { namespace console { namespace detail { +#ifndef DOXYGEN + struct ParsedCommandAddNodeAccess { template @@ -369,6 +371,8 @@ namespace detail { cmdNode.add( CreateParsedCommandOverload::create(fn) ) ); } +#endif + }}} template diff --git a/Console/ParsedCommand.mpp b/Console/ParsedCommand.mpp index a1c5e46..2d645d6 100644 --- a/Console/ParsedCommand.mpp +++ b/Console/ParsedCommand.mpp @@ -95,6 +95,8 @@ public: template detail::ArgumentInfo::type> & arg() const; + void function(Function fn); + protected: private: @@ -131,6 +133,8 @@ public: template detail::ArgumentInfo::type> & arg() const; + void function(Function fn); + protected: private: @@ -178,6 +182,14 @@ arg() const } template +void +senf::console::ParsedCommandOverload:: +function(Function fn) +{ + function_ = fn; +} + +template prefix_ senf::console::ParsedCommandOverload:: ParsedCommandOverload(Function fn) @@ -211,6 +223,14 @@ arg() const } template +void +senf::console::ParsedCommandOverload:: +function(Function fn) +{ + function_ = fn; +} + +template prefix_ senf::console::ParsedCommandOverload:: ParsedCommandOverload(Function fn) diff --git a/Console/Variables.cti b/Console/Variables.cti index f074c5f..c5ca630 100644 --- a/Console/Variables.cti +++ b/Console/Variables.cti @@ -49,8 +49,9 @@ prefix_ Variable const & senf::console::detail::QueryVariable::operato // senf::console::detail::SetVariable template -prefix_ senf::console::detail::SetVariable::SetVariable(Variable & var) - : var_ (var) +prefix_ senf::console::detail::SetVariable::SetVariable(Variable & var, + OnChangeHandler handler) + : var_ (var), handler_ (handler) {} template @@ -66,12 +67,6 @@ prefix_ void senf::console::detail::SetVariable::operator()(Variable c var_ = value; } -template -prefix_ void senf::console::detail::SetVariable::onChange(OnChangeHandler handler) -{ - handler_ = handler; -} - /////////////////////////////////////////////////////////////////////////// // senf::console::ConstVariableAttributor @@ -118,6 +113,15 @@ senf::console::VariableAttributor::typeName(std::string const & name) template prefix_ typename senf::console::VariableAttributor +senf::console::VariableAttributor::onChange(OnChangeHandler handler) +{ + setOverload_.function( + boost::bind(detail::SetVariable(var_, handler),_2)); + return *this; +} + +template +prefix_ typename senf::console::VariableAttributor senf::console::VariableAttributor::doc(std::string const & doc) { ConstVariableAttributor::doc(doc); @@ -135,8 +139,9 @@ senf::console::VariableAttributor::formatter(Formatter formatter) template prefix_ senf::console::VariableAttributor::VariableAttributor(QueryOverload & queryOverload, - SetOverload & setOverload) - : ConstVariableAttributor (queryOverload), setOverload_ (setOverload) + SetOverload & setOverload, + Variable & var) + : ConstVariableAttributor (queryOverload), setOverload_ (setOverload), var_ (var) {} /////////////////////////////////////////////////////////////////////////// @@ -156,7 +161,7 @@ senf::console::detail::VariableNodeCreator::add(DirectoryNode node.add(name, typename detail::QueryVariable::Function( detail::QueryVariable(var))).overload() ); - return VariableAttributor(queryOverload, setOverload); + return VariableAttributor(queryOverload, setOverload, var); } template diff --git a/Console/Variables.hh b/Console/Variables.hh index 5b1b728..f6a2717 100644 --- a/Console/Variables.hh +++ b/Console/Variables.hh @@ -52,6 +52,10 @@ namespace console { senf_console_add_node(DirectoryNode & node, std::string const & name, boost::reference_wrapper var, int); + /** \brief Variable command attributes (const) + + \see VariableAttributor + */ template class ConstVariableAttributor { @@ -72,32 +76,92 @@ namespace console { friend class detail::VariableNodeCreator; }; - - template + + /** \brief Variable command attributes + + Variable commands allow to register any arbitrary variable as a command node. The variable + will be registered as two command overloads: One which takes a single argument of the + variables type to set the variable and another one taking no arguments and just querying the + current variable value. + \code + int var; + ScopedDirectory<> dir; + + dir.add("var", var); + \endcode + + Variables should be registered only with a ScopedDirectory declared in the same scope + (e.g. as a class member for member variables). This ensures, that the variable node is + removed from the tree when the scope is destroyed. + + Since a variable command is added as a combination of two ordinary overloads, it is possible + to register additional overloads with the same name before or after registering the + variable. + + It is also possible, to register a variable read-only. To achieve this, just wrap it with \c + boost::cref(). Such a variable cannot be changed only queried. Therefore, it does not have + the parser() and typeName() attributes. + \code + dir.add("const_var", boost::cref(var)) + \endcode + + \ingroup console_commands + */ + template class VariableAttributor : public ConstVariableAttributor { public: typedef typename detail::SetVariable::Traits::Overload SetOverload; typedef typename detail::ArgumentInfo::Parser Parser; + typedef typename detail::SetVariable::OnChangeHandler OnChangeHandler; typedef OverloadedCommandNode node_type; typedef VariableAttributor return_type; typedef typename ConstVariableAttributor::Formatter Formatter; typedef typename ConstVariableAttributor::QueryOverload QueryOverload; - VariableAttributor parser(Parser parser); - VariableAttributor typeName(std::string const & name); - - VariableAttributor doc(std::string const & doc); - VariableAttributor formatter(Formatter formatter); + VariableAttributor doc(std::string const & doc); ///< Set documentation of the variable + VariableAttributor formatter(Formatter formatter); ///< Set formatter + /**< The \a formatter must be a callable with a signature + compatible with + \code + void formatter(Variable 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. */ + VariableAttributor parser(Parser parser); ///< Set argument 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. */ + VariableAttributor typeName(std::string const & name); ///< Set name of the variable type + VariableAttributor onChange(OnChangeHandler handler); ///< Set change callback + /**< The \a handler callback is called, whenever the value + of the variable is changed. The new value has already + been set, when the callback is called, the old value is + passed to the callback. The callback must have a + signature compatible to + \code + void handler(Variable const & oldValue); + \endcode */ + protected: private: - VariableAttributor(QueryOverload & queryOverload, SetOverload & setOverload); + VariableAttributor(QueryOverload & queryOverload, SetOverload & setOverload, + Variable & var); SetOverload & setOverload_; + Variable & var_; friend class detail::VariableNodeCreator; }; diff --git a/Console/Variables.ih b/Console/Variables.ih index 88b2207..3772fd0 100644 --- a/Console/Variables.ih +++ b/Console/Variables.ih @@ -40,12 +40,15 @@ namespace console { namespace detail { +#ifndef DOXYGEN + template struct QueryVariable { typedef Variable const & Signature (); typedef boost::function Function; typedef detail::ParsedCommandTraits Traits; + typedef Variable const & result_type; QueryVariable(Variable const & var); @@ -61,11 +64,11 @@ namespace detail { typedef boost::function Function; typedef detail::ParsedCommandTraits Traits; typedef boost::function OnChangeHandler; + typedef void result_type; - SetVariable(Variable & var); + SetVariable(Variable & var, OnChangeHandler handler = OnChangeHandler()); void operator()(Variable const & value) const; - void onChange(OnChangeHandler handler); Variable & var_; OnChangeHandler handler_; @@ -87,6 +90,8 @@ namespace detail { Variable & var); }; +#endif + }}} ///////////////////////////////ih.e//////////////////////////////////////// diff --git a/Console/Variables.test.cc b/Console/Variables.test.cc index 6b1e315..502474d 100644 --- a/Console/Variables.test.cc +++ b/Console/Variables.test.cc @@ -46,6 +46,10 @@ namespace { void testFormatter(int value, std::ostream & os) { os << '[' << value << ']'; } + + static bool changed_ (false); + void testCallback(int oldValue) + { changed_ = true; } } BOOST_AUTO_UNIT_TEST(variables) @@ -62,10 +66,12 @@ BOOST_AUTO_UNIT_TEST(variables) .doc("Current blorg limit") .formatter(&testFormatter) .parser(&testParser) - .typeName("number"); + .typeName("number") + .onChange(&testCallback); parser.parse("test/var; test/var 10; test/var", boost::bind( boost::ref(executor), boost::ref(ss), _1 )); BOOST_CHECK_EQUAL( ss.str(), "[5]\n[0]\n" ); + BOOST_CHECK( changed_ ); ss.str(""); dir("var").help(ss);