library. See above links for more:
\code
+ #include <senf/Console.hh>
+
// Define callback function.
void mycommand(std::ostream & os, int foo, int bar)
{
$
</pre>
+ \see \ref console_testserver for a complete example application
+
+ \section intro_init Initialization
+
+ To make the console accessible, it must be initialized when the program is started:
+ \code
+ #include <senf/Console.hh>
+
+ int main(int argc, char * argv [])
+ {
+ // Configure console nodes, add commands ...
+
+ // Start console server
+ senf::console::start(senf::INet4SocketAddress(12345u))
+ .name("myserver");
+
+ // You need to enter the scheduler main-loop for the server to work
+ senf::Scheduler::instance().process();
+
+ // Alternatively enter the main-loop via the PPI
+ // senf::ppi::run();
+ }
+ \endcode
+
+ This will start the server on IPv4 port 12345. The servers name (as displayed in the interactive
+ console prompt) is set to 'myserver'.
+
+ After launching the application, the server can be accessed at the given port:
+ \htmlonly
+ <pre>
+ bash$ telnet localhost 12345
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+
+ myserver:/$ exit
+ Connection closed by foreign host.
+ bash$
+ </pre>
+ \endhtmlonly
\section intro_nodes The node tree
\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
To greatly simplify parsing complex commands, we turn to automatic argument parsing.
- \subsection console_autoadd Adding automatically parsed commands
+ \subsection console_autoadd Adding
Automatically parsed commands are registered by just adding a callback which has the correct
arguments and return-value defined:
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)
</pre>
\endhtmlonly
- \subsection command_overload Command overloading
+ \subsection command_overload Overloading
Automatically parsed commands can be overloaded: You can register multiple commands under the
same name. Each overload is tried in turn until no SyntaxErrorException is raised.
server:/$
</pre>
- \subsection console_attributes Attributes of automatically parsed commands
+ 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
setting additional attributes.
</pre>
\endhtmlonly
-
- \subsection console_argattribpos Passing argument attributes as positional arguments
-
Since most of the time, we only need to set the name and possibly a description for arguments,
there is a shortcut: name and description can be specified as positional arguments in this
- order. So the following will give the exactly same result as the example in the previous section
-
+ order. So the following will give the exactly same result as above:
\code
namespace kw = senf::console::kw;
\endhtmlonly
- \subsection console_boostfn Adding non-function-pointer callable objects
+ \subsection console_boostfn Non-function-pointer commands
It is possible to add other callable objects besides function (and member-function)
pointers. However, since it is not possible to automatically deduce the argument and return
<table class="senf fixedwidth">
- <tr><td>\link senf::console::ParsedArgumentAttributorBase::doc() .doc\endlink ( \e doc )</td><td>Set
- documentation for all overloads</td></tr>
+ <tr><td style="width:14em">\link senf::console::ParsedArgumentAttributorBase::doc() .doc\endlink
+ ( \e doc )</td><td>Set documentation for all overloads</td></tr>
<tr><td>\link senf::console::ParsedArgumentAttributorBase::overloadDoc()
.overloadDoc\endlink ( \e doc )</td><td>Set documentation for a specific overload</td></tr>
<table class="senf fixed width">
- <tr><td>\link senf::console::kw::name kw::name\endlink</td><td>Parameter name</td></tr>
+ <tr><td style="width:14em">\link senf::console::kw::name kw::name\endlink</td><td>Parameter
+ name</td></tr>
<tr><td>\link senf::console::kw::description kw::description\endlink</td><td>One-line
description of the argument</td></tr>
\ref senf::console::kw for a list of all argument attribute keywords
- \section console_memberfn Registering member functions
+ \section console_memberfn Member functions
- 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.
+ Non-static member functions are supported like non-member functions (static member functions are
+ identical to non-members). 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.
+
+
+ \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,
+ Coordinate & 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