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
namespace console {
namespace detail {
+#ifndef DOXYGEN
+
struct ParsedCommandAddNodeAccess
{
template <class Attributor, class Node>
cmdNode.add( CreateParsedCommandOverload<CmdTraits>::create(fn) ) );
}
+#endif
+
}}}
template <class Function>
template <unsigned n>
detail::ArgumentInfo<typename boost::mpl::at_c<arg_types, n>::type> & arg() const;
+ void function(Function fn);
+
protected:
private:
template <unsigned n>
detail::ArgumentInfo<typename boost::mpl::at_c<arg_types, n>::type> & arg() const;
+ void function(Function fn);
+
protected:
private:
}
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)
}
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)
// 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>
var_ = value;
}
-template <class Variable>
-prefix_ void senf::console::detail::SetVariable<Variable>::onChange(OnChangeHandler handler)
-{
- handler_ = handler;
-}
-
///////////////////////////////////////////////////////////////////////////
// senf::console::ConstVariableAttributor<Variable>
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);
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)
{}
///////////////////////////////////////////////////////////////////////////
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>
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
{
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>;
};
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);
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_;
Variable & var);
};
+#endif
+
}}}
///////////////////////////////ih.e////////////////////////////////////////
void testFormatter(int value, std::ostream & os)
{ os << '[' << value << ']'; }
+
+ static bool changed_ (false);
+ void testCallback(int oldValue)
+ { changed_ = true; }
}
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);