Console: Add 'Variable' command 'onChange' implementation
g0dil [Thu, 17 Apr 2008 15:37:52 +0000 (15:37 +0000)]
Console: 'Variable' command documentation

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

Console/Mainpage.dox
Console/ParsedCommand.cti
Console/ParsedCommand.mpp
Console/Variables.cti
Console/Variables.hh
Console/Variables.ih
Console/Variables.test.cc

index 62cffd6..2466616 100644 (file)
     server:/$
     </pre>
 
+    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<void (*)(int)>(&over));
+    senf::console::root()
+        .add("over", static_cast<void (*)(int,int)>(&over));
+    \endcode
+
+
     \subsection console_attributes Attributes
 
     As have seen so far, some documentation is automatically provided. We can add more info, by
     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<Test> dir;
-
-        Test(std::string label) : dir(this), label_ (label) 
-        {
-            dir.add("test4", &Test::test2);
-            dir.add("test4", &Test::test3);
-        }
+        senf::console::ScopedDirectory<Test1> 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:
 
     // ...
 
-    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<Test2> 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
+    <pre>
+    server:/$ test2ob/var
+    0
+    server:/$ test2ob/var 10
+    server:/$ test2ob/var
+    10
+    server:/$ help test2ob
+    Usage:
+        1- var new_value:int
+        2- var
+    server:/$
+    </pre>
+    \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
+    <pre>
+    server:/$ var1
+    0
+    server:/$ help var1
+    Usage:
+        var1
+    server:/$ 
+    </pre>
+    \endhtmlonly
+
+
+    \subsection console_varattr Attributes
+
+    The most important Variable command attributes are
+
+    <table class="senf fixedwidth">
+
+    <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
+    ( \e handler )</td><td>Set change handler</td></tr>
+    
+    </table>
+
+    \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
  */
 
 \f
index 64e8100..0ba97d2 100644 (file)
@@ -310,6 +310,8 @@ namespace senf {
 namespace console {
 namespace detail {
 
+#ifndef DOXYGEN
+
     struct ParsedCommandAddNodeAccess
     {
         template <class Attributor, class Node>
@@ -369,6 +371,8 @@ namespace detail {
             cmdNode.add( CreateParsedCommandOverload<CmdTraits>::create(fn) ) );
     }
 
+#endif
+
 }}}
 
 template <class Function>
index a1c5e46..2d645d6 100644 (file)
@@ -95,6 +95,8 @@ public:
     template <unsigned n>
     detail::ArgumentInfo<typename boost::mpl::at_c<arg_types, n>::type> & arg() const;
 
+    void function(Function fn);
+
 protected:
 
 private:
@@ -131,6 +133,8 @@ public:
     template <unsigned n>
     detail::ArgumentInfo<typename boost::mpl::at_c<arg_types, n>::type> & arg() const;
 
+    void function(Function fn);
+
 protected:
 
 private:
@@ -178,6 +182,14 @@ arg() const
 }
 
 template <class FunctionTraits, class ReturnValue>
+void
+senf::console::ParsedCommandOverload<FunctionTraits, ReturnValue, BOOST_PP_ITERATION() >::
+function(Function fn)
+{
+    function_ = fn;
+}
+
+template <class FunctionTraits, class ReturnValue>
 prefix_
 senf::console::ParsedCommandOverload<FunctionTraits, ReturnValue, BOOST_PP_ITERATION()>::
 ParsedCommandOverload(Function fn)
@@ -211,6 +223,14 @@ arg() const
 }
 
 template <class FunctionTraits>
+void
+senf::console::ParsedCommandOverload<FunctionTraits, void, BOOST_PP_ITERATION() >::
+function(Function fn)
+{
+    function_ = fn;
+}
+
+template <class FunctionTraits>
 prefix_
 senf::console::ParsedCommandOverload<FunctionTraits, void, BOOST_PP_ITERATION() >::
 ParsedCommandOverload(Function fn)
index f074c5f..c5ca630 100644 (file)
@@ -49,8 +49,9 @@ prefix_ Variable const & senf::console::detail::QueryVariable<Variable>::operato
 // senf::console::detail::SetVariable<Variable>
 
 template <class Variable>
-prefix_ senf::console::detail::SetVariable<Variable>::SetVariable(Variable & var)
-    : var_ (var)
+prefix_ senf::console::detail::SetVariable<Variable>::SetVariable(Variable & var,
+                                                                  OnChangeHandler handler)
+    : var_ (var), handler_ (handler)
 {}
 
 template <class Variable>
@@ -66,12 +67,6 @@ prefix_ void senf::console::detail::SetVariable<Variable>::operator()(Variable c
         var_ = value;
 }
 
-template <class Variable>
-prefix_ void senf::console::detail::SetVariable<Variable>::onChange(OnChangeHandler handler)
-{
-    handler_ = handler;
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::ConstVariableAttributor<Variable>
 
@@ -118,6 +113,15 @@ senf::console::VariableAttributor<Variable>::typeName(std::string const & name)
 
 template <class Variable>
 prefix_ typename senf::console::VariableAttributor<Variable>
+senf::console::VariableAttributor<Variable>::onChange(OnChangeHandler handler)
+{
+    setOverload_.function( 
+        boost::bind(detail::SetVariable<Variable>(var_, handler),_2));
+    return *this;
+}
+
+template <class Variable>
+prefix_ typename senf::console::VariableAttributor<Variable>
 senf::console::VariableAttributor<Variable>::doc(std::string const & doc)
 {
     ConstVariableAttributor<Variable>::doc(doc);
@@ -135,8 +139,9 @@ senf::console::VariableAttributor<Variable>::formatter(Formatter formatter)
 template <class Variable>
 prefix_
 senf::console::VariableAttributor<Variable>::VariableAttributor(QueryOverload & queryOverload,
-                                                                SetOverload & setOverload)
-    : ConstVariableAttributor<Variable> (queryOverload), setOverload_ (setOverload)
+                                                                SetOverload & setOverload,
+                                                                Variable & var)
+    : ConstVariableAttributor<Variable> (queryOverload), setOverload_ (setOverload), var_ (var)
 {}
 
 ///////////////////////////////////////////////////////////////////////////
@@ -156,7 +161,7 @@ senf::console::detail::VariableNodeCreator<Variable,isConst>::add(DirectoryNode
         node.add(name, typename detail::QueryVariable<Variable>::Function(
                      detail::QueryVariable<Variable>(var))).overload() );
 
-    return VariableAttributor<Variable>(queryOverload, setOverload);
+    return VariableAttributor<Variable>(queryOverload, setOverload, var);
 }
 
 template <class Variable>
index 5b1b728..f6a2717 100644 (file)
@@ -52,6 +52,10 @@ namespace console {
     senf_console_add_node(DirectoryNode & node, std::string const & name, 
                           boost::reference_wrapper<Variable> var, int);
 
+    /** \brief Variable command attributes (const)
+        
+        \see VariableAttributor
+     */
     template <class Variable>
     class ConstVariableAttributor
     {
@@ -72,32 +76,92 @@ namespace console {
 
         friend class detail::VariableNodeCreator<Variable const>;
     };
-
-    template <class Variable>
+    /** \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 Variable>
     class VariableAttributor
         : public ConstVariableAttributor<Variable>
     {
     public:
         typedef typename detail::SetVariable<Variable>::Traits::Overload SetOverload;
         typedef typename detail::ArgumentInfo<typename SetOverload::arg1_type>::Parser Parser;
+        typedef typename detail::SetVariable<Variable>::OnChangeHandler OnChangeHandler;
         typedef OverloadedCommandNode node_type;
         typedef VariableAttributor return_type;
 
         typedef typename ConstVariableAttributor<Variable>::Formatter Formatter;
         typedef typename ConstVariableAttributor<Variable>::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<Variable>;
     };
index 88b2207..3772fd0 100644 (file)
@@ -40,12 +40,15 @@ namespace console {
 
 namespace detail {
 
+#ifndef DOXYGEN
+
     template <class Variable>
     struct QueryVariable
     {
         typedef Variable const & Signature ();
         typedef boost::function<Signature> Function;
         typedef detail::ParsedCommandTraits<Signature> Traits;
+        typedef Variable const & result_type;
 
         QueryVariable(Variable const & var);
 
@@ -61,11 +64,11 @@ namespace detail {
         typedef boost::function<Signature> Function;
         typedef detail::ParsedCommandTraits<Signature> Traits;
         typedef boost::function<void (Variable const &)> 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////////////////////////////////////////
index 6b1e315..502474d 100644 (file)
@@ -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<void>( 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);