Socket: Add additional port-only constructor for INet[46]SocketAddress
g0dil [Wed, 23 Apr 2008 15:31:18 +0000 (15:31 +0000)]
Console: Add Console.hh
Console: Add testServer as example to the documentation
Console: Fix overload resolution when adding manually parsed commands
Console: Remove Server::instance() to be more flexible with future extensions

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

12 files changed:
Console/Console.hh [new file with mode: 0644]
Console/Doxyfile
Console/Example.dox [new file with mode: 0644]
Console/Mainpage.dox
Console/Parse.hh
Console/ParsedCommand.ih
Console/Server.cc
Console/Server.hh
Console/testServer.cc
Socket/Protocols/INet/INetAddressing.cc
Socket/Protocols/INet/INetAddressing.cci
Socket/Protocols/INet/INetAddressing.hh

diff --git a/Console/Console.hh b/Console/Console.hh
new file mode 100644 (file)
index 0000000..88149ed
--- /dev/null
@@ -0,0 +1,53 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Console public header */
+
+#ifndef HH_Console_
+#define HH_Console_ 1
+
+// Custom includes
+
+//#include "Console.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+#include "Server.hh"
+#include "ParsedCommand.hh"
+#include "ScopedDirectory.hh"
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Console.cci"
+//#include "Console.ct"
+//#include "Console.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 1075516..4b9bc91 100644 (file)
@@ -2,6 +2,8 @@
 
 PROJECT_NAME = libConsole
 GENERATE_TAGFILE = doc/Console.tag
+EXAMPLE_PATH = .
+EXCLUDE = testServer.cc
 
 TAGFILES = \
     "$(TOPDIR)/Socket/doc/Socket.tag" \
diff --git a/Console/Example.dox b/Console/Example.dox
new file mode 100644 (file)
index 0000000..be71f69
--- /dev/null
@@ -0,0 +1,95 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \page console_testserver Test Server
+
+    \dontinclude testServer.cc
+
+    You can find this code in the \c testServer.cc source file.
+
+    \skip Custom
+    \skip #include
+    \until namespace
+
+    Here we see the necessary include files. For console support only <tt>senf/Console.hh</tt> needs
+    to be included. This will pull in all necessary definitions.
+
+    We declare an alias for the senf::console::kw namespace which we will use further down for the
+    keyword arguments.
+
+    \until echo
+    \until }
+    \until }
+
+    The first callback \c echo utilizes \link console_manualparse manual argument
+    parsing\endlink. You should always refer to the iterator types as shown here since this will be
+    safe from future changes.
+
+    The following \c struct \c TestObject is used to show how member functions and objects are used
+    in the console.
+
+    \until dir
+
+    Important is the definition of the <b>public</b> senf::console::ScopedDirectory member called \c
+    dir. This member can be used later to add the class to the node tree. Here we just register a
+    single function \c vat (with \link console_autoparse automatic argument parsing\endlink) and
+    set some command attributes.
+
+    \until };
+
+    The \c shutdownServer callback terminates the server.
+
+    \until }
+
+    This happens in two steps: First the \c terminate() call tells the scheduler to leave it's
+    main-loop after shutdownServer returns (which is ultimately called via the console server from
+    the scheduler). Throwing a senf::console::Executor::ExitException is like entering the \c exit
+    built-in command at the console.
+    
+    \until network
+    \until Example
+
+    The \c main routine enables more verbose console logging and adds some directories and callbacks
+    to the tree so we have some stuff to play around with.
+
+    The following few lines of code instantiate a \c TestObject instance and add this object's
+    directory node to the tree
+
+    \until Example
+    
+    Now we are ready to start the server and enter the Scheduler main-loop
+
+    \until }
+ */
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -U doc"
+// mode: flyspell
+// mode: auto-fill
+// End:
index 1d09056..8226247 100644 (file)
@@ -51,6 +51,8 @@
     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
 
     }
 
     void senf_console_parse_argument(senf::console::ParseCommandInfo::TokensRange const & tokens,
-                                     MyClass & out)
+                                     Coordinate & out)
     {
         if (tokens.size() != 2)
             throw SyntaxErrorException("parameter syntax error");
index 295f4a6..a37ed01 100644 (file)
@@ -239,6 +239,14 @@ namespace console {
         \li the arguments. Every argument consists of a range of ArgumentToken instances.
 
         \ingroup console_parser
+
+        \todo Completely change the 'arguments()' member implementation: let the parser just
+            generate a flat list of tokens and implement an 'argument iterator' with the following
+            features: 1. return argument ranges, automatically detecting paranthesis 2. trying to
+            increment the iterator beyond it's range just throws an argument syntax error. For this
+            to work, the parser needs to not drop the outermost '()' pair 3. detect bad paranthesis
+            (should not be necessary since the parser already does this). This allows to use this
+            same iterator to parse nested complex arguments.
       */
     class ParseCommandInfo
     {
index d9c2610..90231c2 100644 (file)
@@ -109,14 +109,14 @@ namespace detail {
         typedef typename Traits::arg1_type type;
     };
 
-    template <class Function, bool isFN=senf::is_any_function<Function>::value>
-    struct ParsedCommandTraits
+    template <class FnunctionP, class Function, bool isFN=boost::is_function<Function>::value>
+    struct ParsedCommandTraits_i
     {};
 
-    template <class Fn>
-    struct ParsedCommandTraits<Fn, true>
+    template <class FunctionP, class Function>
+    struct ParsedCommandTraits_i<FunctionP, Function, true>
     {
-        typedef Fn base_type;
+        typedef FunctionP base_type;
         typedef typename senf::remove_any_pointer<base_type>::type function_type;
         typedef boost::function_traits<function_type> base_traits;
         typedef typename FirstArgType<base_traits>::type first_arg_type;
@@ -139,6 +139,17 @@ namespace detail {
         typedef ParsedArgumentAttributor<Overload> Attributor;
     };
 
+    // Disable auto-parsing for ParseCommandInfo arg -> register manually parsed command
+    template <class FunctionP>
+    struct ParsedCommandTraits_i<FunctionP, void (std::ostream &, ParseCommandInfo const &), true>
+    {};
+
+    template <class FunctionP>
+    struct ParsedCommandTraits
+        : public ParsedCommandTraits_i< FunctionP, 
+                                        typename senf::remove_any_pointer<FunctionP>::type >
+    {};
+
     struct ParsedCommandAddNodeAccess;
 
 #endif
index 856a67c..53db4e7 100644 (file)
@@ -64,26 +64,20 @@ prefix_ senf::console::Server &
 senf::console::Server::start(senf::INet4SocketAddress const & address)
 {
     senf::TCPv4ServerSocketHandle handle (address);
-    senf::console::Server::start(handle);
+    Server & server (senf::console::Server::start(handle));
     SENF_LOG((Server::SENFLogArea)(log::NOTICE)( 
                  "Console server started at " << address ));
-    return instance();
+    return server;
 }
 
 prefix_ senf::console::Server &
 senf::console::Server::start(senf::INet6SocketAddress const & address)
 {
     senf::TCPv6ServerSocketHandle handle (address);
-    senf::console::Server::start(handle);
+    Server & server (senf::console::Server::start(handle));
     SENF_LOG((Server::SENFLogArea)(log::NOTICE)( 
                  "Console server started at " << address ));
-    return instance();
-}
-
-prefix_ senf::console::Server & senf::console::Server::instance()
-{
-    SENF_ASSERT( instancePtr() );
-    return *instancePtr();
+    return server;
 }
 
 prefix_ boost::scoped_ptr<senf::console::Server> & senf::console::Server::instancePtr()
@@ -95,13 +89,14 @@ prefix_ boost::scoped_ptr<senf::console::Server> & senf::console::Server::instan
     return instance;
 }
 
-prefix_ void senf::console::Server::start(ServerHandle handle)
+prefix_ senf::console::Server & senf::console::Server::start(ServerHandle handle)
 {
     // Uah .... ensure the scheduler is created before the instance pointer so it get's destructed
     // AFTER it.
     (void) senf::Scheduler::instance();
     SENF_ASSERT( ! instancePtr() );
     instancePtr().reset(new Server(handle));
+    return * instancePtr();
 }
 
 prefix_ senf::console::Server::Server(ServerHandle handle)
@@ -118,7 +113,7 @@ prefix_ senf::console::Server::~Server()
 prefix_ void senf::console::Server::newClient(Scheduler::EventId event)
 {
     ServerHandle::ClientSocketHandle client (handle_.accept());
-    boost::intrusive_ptr<Client> p (new Client(client, name_));
+    boost::intrusive_ptr<Client> p (new Client(*this, client, name_));
     clients_.insert( p );
     SENF_LOG(( "Registered new client " << p.get() ));
 }
@@ -133,9 +128,10 @@ prefix_ void senf::console::Server::removeClient(Client & client)
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::Client
 
-prefix_ senf::console::Client::Client(ClientHandle handle, std::string const & name)
-    : out_t(handle), senf::log::IOStreamTarget(out_t::member),
-      handle_ (handle), name_ (name), promptLen_(0)
+prefix_ senf::console::Client::Client(Server & server, ClientHandle handle,
+                                      std::string const & name)
+    : out_t(handle), senf::log::IOStreamTarget(out_t::member), server_ (server),
+      handle_ (handle), name_ (name), promptLen_(0) 
 {
     showPrompt();
     ReadHelper<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
@@ -149,7 +145,7 @@ prefix_ senf::console::Client::~Client()
 prefix_ void senf::console::Client::stopClient()
 {
     // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS
-    Server::instance().removeClient(*this);
+    server_.removeClient(*this);
 }
 
 prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr helper)
index 3b7a93e..59111e6 100644 (file)
@@ -63,6 +63,10 @@ namespace console {
             our own little host-name cache. When the name is not found, we ask the resolver to
             resolve it and call 'resume' when the name is found. Since it is in the cache now, the
             command will now complete.
+        
+        \implementation We do \e not provide an \c instance() member so we can easily later extend
+            the server to allow registering more than one instance, e.g. with each instance on a
+            differently firewalled port and with different security restrictions.
       */
     class Server
         : boost::noncopyable
@@ -83,8 +87,6 @@ namespace console {
                                         ///< Start server on given IPv4 address/port
         static Server & start(senf::INet6SocketAddress const & address);
                                         ///< Start server on given IPv6 address/port
-        static Server & instance();
-
         void name(std::string const & name); ///< Set server name
                                         /**< This information is used in the prompt string. */
 
@@ -93,7 +95,7 @@ namespace console {
     private:
         Server(ServerHandle handle);
 
-        static void start(ServerHandle handle);
+        static Server & start(ServerHandle handle);
         static boost::scoped_ptr<Server> & instancePtr();
 
         void newClient(Scheduler::EventId event);
@@ -137,7 +139,7 @@ namespace console {
     protected:
         
     private:
-        Client(ClientHandle handle, std::string const & name);
+        Client(Server & server, ClientHandle handle, std::string const & name);
 
         void clientData(ReadHelper<ClientHandle>::ptr helper);
         void showPrompt();
@@ -146,6 +148,7 @@ namespace console {
                              std::string const & area, unsigned level, 
                              std::string const & message);
         
+        Server & server_;
         ClientHandle handle_;
         std::string tail_;
         CommandParser parser_;
index fe42969..0ede865 100644 (file)
 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 /** \file
-    \brief test non-inline non-template implementation */
-
-//#include "test.hh"
-//#include "test.ih"
+    \brief testServer implementation */
 
 // Custom includes
 #include <iostream>
-#include "Server.hh"
-#include "Node.hh"
-#include "ScopedDirectory.hh"
-#include "../Scheduler/Scheduler.hh"
-#include "../Utils/Logger/SenfLog.hh"
+#include <senf/Console.hh>
+#include <senf/Scheduler/Scheduler.hh>
 
-//#include "test.mpp"
-#define prefix_
-///////////////////////////////cc.p////////////////////////////////////////
+namespace kw = senf::console::kw;
 
-namespace {
-    
-    void fn(std::ostream & output, senf::console::ParseCommandInfo const & command) {
-        typedef senf::console::ParseCommandInfo::ArgumentsRange::iterator iterator;
-        iterator i (command.arguments().begin());
-        iterator i_end (command.arguments().end());
-        for (; i != i_end; ++i) {
-            iterator::value_type::iterator j (i->begin());
-            iterator::value_type::iterator j_end (i->end());
-            for (; j != j_end; ++j) 
-                output << j->value() << ' ';
-        }
-        output << "\n";
+void echo(std::ostream & output, senf::console::ParseCommandInfo const & command)
+{
+    typedef senf::console::ParseCommandInfo::ArgumentsRange::iterator iterator;
+    iterator i (command.arguments().begin());
+    iterator i_end (command.arguments().end());
+    for (; i != i_end; ++i) {
+        iterator::value_type::iterator j (i->begin());
+        iterator::value_type::iterator j_end (i->end());
+        for (; j != j_end; ++j) 
+            output << j->value() << ' ';
     }
+    output << "\n";
+}
 
-    struct TestObject
-    {
-        senf::console::ScopedDirectory<TestObject> dir;
-
-        TestObject() : dir(this) {
-            dir.add("blub", &TestObject::blub)
-                .doc("Example of a member function");
-        }
-        
-        void blub(std::ostream & output, senf::console::ParseCommandInfo const &) {
-            output << "blub\n";
+struct TestObject
+{
+    senf::console::ScopedDirectory<TestObject> dir;
+
+    TestObject() 
+        : dir(this) 
+        {
+            dir.add("vat", &TestObject::vat)
+                .arg("vat", "VAT in %", kw::default_value = 19)
+                .arg("amount", "Amount including VAT")
+                .doc("Returns the amount of {vat}-% VAT included in {amount}");
         }
-    };
 
-    void shutdownServer(std::ostream &, senf::console::ParseCommandInfo const &)
-    {
-        senf::Scheduler::instance().terminate();
-        throw senf::console::Executor::ExitException();
-    }
+    double vat (int vat, double amount) 
+        {
+            return amount * vat/(100.0+vat);
+        }
+};
 
+void shutdownServer()
+{
+    senf::Scheduler::instance().terminate();
+    throw senf::console::Executor::ExitException();
 }
 
-int main(int, char const **)
+int main(int, char **)
 {
     senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >();
 
@@ -93,24 +87,20 @@ int main(int, char const **)
         .add("shutdown", &shutdownServer)
         .doc("Terminate server application");
     senf::console::root()["network"]
-        .add("route", &fn)
-        .doc("Example of a directly registered function");
+        .add("echo", &echo)
+        .doc("Example of a function utilizing manual argument parsing");
 
     TestObject test;
     senf::console::root()
         .add("testob", test.dir)
         .doc("Example of an instance directory");
 
-    senf::console::Server::start( senf::INet4SocketAddress("0.0.0.0:23232") )
+    senf::console::Server::start( senf::INet4SocketAddress(23232) )
         .name("testServer");
 
     senf::Scheduler::instance().process();
 }
 
-///////////////////////////////cc.e////////////////////////////////////////
-#undef prefix_
-//#include "test.mpp"
-
 \f
 // Local Variables:
 // mode: c++
index 6bc79e5..d49f4de 100644 (file)
@@ -65,6 +65,12 @@ prefix_ senf::INet4SocketAddress::INet4SocketAddress(INet4Address const & addr,
     port(p);
 }
 
+prefix_ senf::INet4SocketAddress::INet4SocketAddress(unsigned p)
+{
+    clear();
+    port(p);
+}
+
 prefix_ void senf::INet4SocketAddress::clear()
 {
     ::memset(&addr_,0,sizeof(addr_));
index 3f389aa..3687001 100644 (file)
@@ -120,6 +120,12 @@ prefix_ senf::INet6SocketAddress::INet6SocketAddress(INet6Address const & addr,
     assignIface(iface);
 }
 
+prefix_ senf::INet6SocketAddress::INet6SocketAddress(unsigned port)
+{
+    clear();
+    sockaddr_.sin6_port = htons(port);
+}
+
 prefix_ senf::INet6Address senf::INet6SocketAddress::address()
     const
 {
index 929d888..57efbe4 100644 (file)
@@ -60,6 +60,10 @@ namespace senf {
           public senf::comparable_safe_bool<INet4SocketAddress>
     {
     public:
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
         INet4SocketAddress();
         explicit INet4SocketAddress(std::string const & addr); ///< Set address and port
                                         /**< This constructor expects a string of the form
@@ -77,6 +81,13 @@ namespace senf {
                                         /**< \param[in] addr IP address
                                              \param[in] port port number */
 
+        explicit INet4SocketAddress(unsigned port);
+                                        ///< Set port, address is set to 0.0.0.0
+                                        /**< \param[in] port port number */
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
         bool operator==(INet4SocketAddress const & other) const;
                                         ///< Check INet4SocketAddress for equality
 
@@ -146,9 +157,6 @@ namespace senf {
     {
     public:
         ///////////////////////////////////////////////////////////////////////////
-        // Types
-
-        ///////////////////////////////////////////////////////////////////////////
         ///\name Structors and default members
         ///@{
 
@@ -168,6 +176,10 @@ namespace senf {
                                         ///< Initialize from address and port
         INet6SocketAddress(INet6Address const & addr, unsigned port, std::string const & iface);
                                         ///< Initialize explicitly from given parameters
+        explicit INet6SocketAddress(unsigned port);
+                                        ///< Initialize from port and set to 'unspecified' addr
+                                        /**< The address is set to [::]
+                                             \param[in] port port number  */
 
         ///@}
         ///////////////////////////////////////////////////////////////////////////