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.
+
+ The next callback accesses the client instance directly to manipulate the logging:
- \until Example
+ \until }
+
+ The senf::console::Client instance can be accessed using the senf::console::Client::get() helper
+ via the output stream. Since every Client is a senf::log::Target, we can route arbitrary log
+ messages to the console instance.
+
+ We now define \c main() which initializes the node tree and starts the console server
+
+ \until route
+
+ Here we just setup more verbose logging and set \c SIGPIPE signals to be ignored. \c SIGPIPE's
+ are a pain and really should be disabled.
+
+ \until settings
- 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.
+ This shows, how to set the top-level documentation and create a new subdirectory directly
- The following few lines of code instantiate a \c TestObject instance and add this object's
- directory node to the tree
+ \until mkdir
+ Here we create another new directory but save a reference so we can later access the node
+ directly. All the add commands return such a node reference of the correct type (this is a lie,
+ but it works like this anyways and it's an implementation detail that must not concern you
+ here).
+
+ This way of stroing a refernce is not bad, but even better is to use a \c
+ senf::console::ScopedDirectory<> for this
+
+ \until functions
+
+ This will automatically remove the node from the tree when the senf::console::ScopedDiretory
+ instance is destroyed and keeps the node alive even when unlinked from the tree (a plain
+ reference becomes invalid when anyone later unlinks the node from the tree). This is much safer
+ and is the preferred way to keep a hold on a directory.
+
+ The next statements add commands to the various directories declared so far
+
\until Example
+
+ We now continue by creating an instance of our test class \c TestObject
- Now we are ready to start the server and enter the Scheduler main-loop
+ \until Example
+
+ We add that objects directory to the \c test dir. We now have created a directory structure like
+ tis:
+ <pre>
+ /
+ console/
+ showlog
+ server/
+ shutdown
+ test/
+ echo
+ testob/
+ vat
+ </pre>
+
+ We now start the server (giving it a nice descriptive name) and run the scheduler.
\until }
*/
\endcode
\see \ref console_parser
+
+
+ \section console_misc Further features
+
+ \subsection console_serverclient Server and Client objects
+
+ The senf::console::Server and senf::console::Client objects offer further API calls. To access
+ the server instance you need to store away the senf::console::Server reference returned when
+ starting the server so you can later refer to it:
+ \code
+ int main(int, char**)
+ {
+ senf::console::Server & server ( senf::console::start( ... ) );
+
+ // Do something ...
+
+ server.stop()
+ }
+ \endcode
+
+ The client instance can be accessed via the \c std::ostream arg of any command callback
+ \code
+ void someCallback(std::ostream & os, ... )
+ {
+ senf::console::Client & client (senf::console::Client::get(os));
+
+ // Use the client's log target
+ client.route<senf::log::Debug, senf::Log::IMPORTANT>();
+ }
+ \endcode
+
+ \see
+ senf::console::Server for the Server API \n
+ <a href="classsenf_1_1console_1_1Client-members.html">senf::console::Client / List of all
+ members</a> for the Client API
+
+
+ \subsection console_shell Features of the interactive console shell
+
+ The interactive shell will use the GNU readline library for the first connected
+ instance. Further users will not have access to this functionality since GNU readline is
+ completely non-reentrant.
+
+ The shell supports auto-cd and auto-completion: If you enter the name of a directory at the
+ prompt, the console will change to that directory. With auto-completion, any unique beginning of
+ a path component will be completed automatically and transparently to th corresponding full
+ name.
*/
/** \defgroup console_commands Supported command types
int readline_getc_function(FILE *)
{
if (senf::console::detail::ReadlineClientReader::active())
- return senf::console::detail::ReadlineClientReader::instance().getc();
+ return senf::console::detail::ReadlineClientReader::instance().getc()
else
return -1;
}
void readline_callback(char * input)
{
- if (senf::console::detail::ReadlineClientReader::active() && input)
- return senf::console::detail::ReadlineClientReader::instance().callback(
- std::string(input) );
+ if (senf::console::detail::ReadlineClientReader::active()) {
+ if (input)
+ return senf::console::detail::ReadlineClientReader::instance().callback(
+ std::string(input) );
+ else // input == 0 -> EOF (or Ctrl-D)
+ senf::console::detail::ReadlineClientReader::instance().eof();
+ }
}
ssize_t readline_cookie_write_function(void * cookie, char const * buffer, size_t size)
void readline_deprep_term()
{}
+ int restart_line(int count, int key)
+ {
+ rl_kill_full_line(count, key);
+ rl_crlf();
+ rl_forced_update_display();
+ return 0;
+ }
+
}
prefix_ senf::console::detail::ReadlineClientReader::ReadlineClientReader(Client & client)
rl_deprep_term_function = &readline_deprep_term;
rl_getc_function = &readline_getc_function;
rl_bind_key('\t', &rl_insert);
+ rl_bind_key('\x03', &restart_line);
using_history();
// Don't ask me, where I found this ...
stopClient();
return;
}
- ch_ = ch;
+ ch_ = static_cast<unsigned char>(ch);
if (skipChars_ > 0)
--skipChars_;
- else if (ch_ == static_cast<char>(0xff))
+ else if (ch_ == 0xff)
skipChars_ = 2;
else if (ch_ != 0)
rl_callback_read_char();
terminate_ = true;
}
+prefix_ void senf::console::detail::ReadlineClientReader::eof()
+{
+ stream() << '\n' << std::flush;
+ stopClient();
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::console::detail::SafeReadlineClientReader
void callback(std::string line);
void write(std::string text);
void terminate();
+ void eof();
private:
virtual void v_disablePrompt();
This class provides an interactive console TCP server.
- \todo Add readline support
\todo Add interactivity detection using timeout
\idea To support blocking commands, we could give the Client 'suspend()' and 'resume()'
members. suspend() would probably throw some kind of exception to transfer control back
void enableLogging(std::ostream & os)
{
- senf::console::Client::get(os).route<senf::SenfLog,senf::log::NOTICE>();
+ senf::console::Client::get(os).route<senf::log::NOTICE>();
}
int main(int, char **)
{
::signal(SIGPIPE, SIG_IGN);
- senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >();
+ senf::log::ConsoleTarget::instance().route< senf::log::VERBOSE >();
senf::console::root()
.doc("This is the console test application");
+
senf::console::root()
.mkdir("console")
.doc("Console settings");
+
+ senf::console::DirectoryNode & serverDir (
+ senf::console::root()
+ .mkdir("server")
+ .doc("server commands") );
+
+ senf::console::ScopedDirectory<> testDir;
senf::console::root()
- .mkdir("test")
+ .add("test", testDir)
.doc("Test functions");
- senf::console::root()
- .mkdir("server");
senf::console::root()["console"]
.add("showlog", &enableLogging)
.doc("Enable display of log messages on the current console");
- senf::console::root()["server"]
+
+ serverDir
.add("shutdown", &shutdownServer)
.doc("Terminate server application");
- senf::console::root()["test"]
+
+ testDir
.add("echo", &echo)
.doc("Example of a function utilizing manual argument parsing");
TestObject test;
- senf::console::root()["test"]
+ testDir
.add("testob", test.dir)
.doc("Example of an instance directory");
"$(TOPDIR)/Packets/doc/Packets.tag" \
"$(TOPDIR)/Packets/DefaultBundle/doc/DefaultBundle.tag" \
"$(TOPDIR)/Socket/doc/Socket.tag" \
- "$(TOPDIR)/Utils/doc/Utils.tag"
+ "$(TOPDIR)/Utils/doc/Utils.tag" \
+ "$(TOPDIR)/senfscons/doc/senfscons.tag"
The most simple way to build using SENF is to use a very simple SCons helper which automatically
supports debug and final builds, uses SENF either centrally installed or locally built and has
- some other nice features. See \ref senfutil for more info and an example for this utility.
+ some other nice features. See <a
+ href="../../senfscons/doc/html/index.html#senfutil_overview">Building Projects using SENF</a>
+ for more info and an example for this utility.
\see \ref senf_components \n
\ref senf_overview
#ifndef DOXYGEN
-template <class Stream>
+template <class A1>
prefix_ void senf::log::Target::route(action_t action, int index)
{
- route(&Stream::instance(), 0, NONE::value, action, index);
+ route<A1,mpl::nil,mpl::nil>(action, index);
}
-template <class Stream, class Level>
-prefix_ void senf::log::Target::
-route(action_t action, int index,
- typename boost::enable_if< boost::is_convertible<Level*, detail::LevelBase *> >::type *)
-{
- route(&Stream::instance(), 0, Level::value, action, index);
-}
-
-template <class Stream, class Area>
-prefix_ void senf::log::Target::
-route(action_t action, int index,
- typename boost::enable_if< boost::is_convertible<Area*, detail::AreaBase *> >::type *)
-{
- route(&Stream::instance(), &Area::instance(), NONE::value, action, index);
-}
-
-template <class Stream, class AreaClass>
-prefix_ void senf::log::Target::
-route(action_t action, int index,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *, detail::AreaBase *> >::type *)
-{
- route(&Stream::instance(), &AreaClass::SENFLogArea::instance(), NONE::value, action, index);
-}
-
-template <class Stream, class Area, class Level>
-prefix_ void senf::log::Target::
-route(action_t action, int index,
- typename boost::enable_if< boost::is_convertible<Area *, detail::AreaBase *> >::type *)
+template <class A1, class A2>
+prefix_ void senf::log::Target::route(action_t action, int index)
{
- route(&Stream::instance(), &Area::instance(), Level::value, action, index);
+ route<A1,A2,mpl::nil>(action, index);
}
-template <class Stream, class AreaClass, class Level>
-prefix_ void senf::log::Target::
-route(action_t action, int index,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *, detail::AreaBase *> >::type *)
+template <class A1, class A2, class A3>
+prefix_ void senf::log::Target::route(action_t action, int index)
{
- route(&Stream::instance(), &AreaClass::SENFLogArea::instance(), Level::value, action, index);
+ typedef detail::RouteParameters<A1,A2,A3> Params;
+ route( detail::InstanceP<typename Params::Stream, detail::StreamBase>::value(),
+ detail::InstanceP<typename Params::Area, detail::AreaBase>::value(),
+ Params::Level::value,
+ action, index);
}
-// senf::log::target::ununroute
-
-template <class Stream>
+template <class A1>
prefix_ void senf::log::Target::unroute(action_t action)
{
- unroute(&Stream::instance(), 0, NONE::value, action);
+ unroute<A1,mpl::nil,mpl::nil>(action);
}
-template <class Stream, class Level>
-prefix_ void senf::log::Target::
-unroute(action_t action,
- typename boost::enable_if< boost::is_convertible<Level*, detail::LevelBase *> >::type *)
-{
- unroute(&Stream::instance(), 0, Level::value, action);
-}
-
-template <class Stream, class Area>
-prefix_ void senf::log::Target::
-unroute(action_t action,
- typename boost::enable_if< boost::is_convertible<Area*, detail::AreaBase *> >::type *)
-{
- unroute(&Stream::instance(), &Area::instance(), NONE::value, action);
-}
-
-template <class Stream, class AreaClass>
-prefix_ void senf::log::Target::
-unroute(action_t action,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *, detail::AreaBase *> >::type *)
-{
- unroute(&Stream::instance(), &AreaClass::SENFLogArea::instance(), NONE::value, action);
-}
-
-template <class Stream, class Area, class Level>
-prefix_ void senf::log::Target::
-unroute(action_t action,
- typename boost::enable_if< boost::is_convertible<Area *, detail::AreaBase *> >::type *)
+template <class A1, class A2>
+prefix_ void senf::log::Target::unroute(action_t action)
{
- unroute(&Stream::instance(), &Area::instance(), Level::value, action);
+ unroute<A1,A2,mpl::nil>(action);
}
-template <class Stream, class AreaClass, class Level>
-prefix_ void senf::log::Target::
-unroute(action_t action,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *, detail::AreaBase *> >::type *)
+template <class A1, class A2, class A3>
+prefix_ void senf::log::Target::unroute(action_t action)
{
- unroute(&Stream::instance(), &AreaClass::SENFLogArea::instance(), Level::value, action);
+ typedef detail::RouteParameters<A1,A2,A3> Params;
+ unroute( detail::InstanceP<typename Params::Stream, detail::StreamBase>::value(),
+ detail::InstanceP<typename Params::Area, detail::AreaBase>::value(),
+ Params::Level::value,
+ action);
}
#endif
implementation is more efficient and utilizes a routing cache).
Each routing entry consists of the following parameters
- \li (mandatory) \e stream. The entry will match only messages directed at that stream
+ \li (optional) \e stream. The entry will match only messages directed at that stream
\li (optional) \e area. If the area is specified, only messages directed at that area are
matched, otherwise any area will be allowed
\li (optional) \e level. If the log level is specified, messages will be accepted if their
template <class Stream, class Area, class Level> void route(
action_t action = ACCEPT, int index = -1); ///< Add route (static)
/**< Add a route for the given combination of \a Stream, \a
- Area and \a Level. The \a Stream parameter is mandatory
- while either \a Area or \a Level are optional (the
- template signature is shown simplified here):
+ Area and \a Level. All parameters (\a Stream, \a Area
+ and \a Level) are optional (the template signature is
+ shown simplified here). Examples:
\code
+ target.route<SomeLevel>();
target.route<SomeStream>();
target.route<SomeStream, SomeLevel>();
target.route<SomeStream, SomeArea>();
See the class description for information on the \a
action and \a index parameters
- \tparam Stream mandatory stream to match
- \tparam Area optional area to match
- \tparam Level optional level, matches messages with
+ \tparam Stream stream to match
+ \tparam Area area to match
+ \tparam Level level, matches messages with
at least the given level.
\param[in] action routing action to take
\param[in] index position of new route in the routing
unsigned level = NONE::value, action_t action = ACCEPT, int index = -1);
///< Add route (dynamic)
/**< Add a route for the given combination of \a stream, \a
- area and \a level. The \a stream parameter is mandatory
- while either \a area or \a level may be left
- unspecified by setting them to the empty string or
+ area and \a level. All parameters (\a Stream, \a Area
+ and \a Level) are optional and may be omitted by
+ setting them to the empty string or the
senf::log::NONE::value respectively.
See the class description for information on the \a
\throws InvalidAreaException if the given \a area is
not found in the AreaRegistry
- \param[in] stream mandatory stream to match
- \param[in] area optional area to match
- \param[in] level optional level, matches messages with
- at least the given level.
+ \param[in] stream stream to match
+ \param[in] area area to match
+ \param[in] level level, matches messages with at least
+ the given level.
\param[in] action routing action to take
\param[in] index position of new route in the routing
table */
found, it will be removed, otherwise the call will be
ignored
- \tparam Stream mandatory stream to match
- \tparam Area optional area to match
- \tparam Level optional level, matches messages with
+ \tparam Stream stream to match
+ \tparam Area area to match
+ \tparam Level level, matches messages with
at least the given level.
\param[in] action routing action to take */
found, it will be removed, otherwise the call will be
ignored
- \param[in] stream mandatory stream to match
- \param[in] area optional area to match
- \param[in] level optional level, matches messages with
- at least the given level.
+ \param[in] stream stream to match
+ \param[in] area area to match
+ \param[in] level level, matches messages with at least
+ the given level.
\param[in] action routing action to take */
void unroute(int index=-1); ///< Remove route (indexed)
/**< This call will remove the route with the given index.
# ifndef DOXYGEN
- template <class Stream> void route(
- action_t action = ACCEPT, int index = -1);
- template <class Stream, class Level> void route(
- action_t action = ACCEPT, int index = -1,
- typename boost::enable_if< boost::is_convertible<Level*,
- detail::LevelBase *> >::type * = 0);
- template <class Stream, class Area> void route(
- action_t action = ACCEPT, int index = -1,
- typename boost::enable_if< boost::is_convertible<Area*,
- detail::AreaBase *> >::type * = 0);
- template <class Stream, class AreaClass> void route(
- action_t action = ACCEPT, int index = -1,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
- detail::AreaBase *> >::type * = 0);
- template <class Stream, class Area, class Level> void route(
- action_t action = ACCEPT, int index = -1,
- typename boost::enable_if< boost::is_convertible<Area *,
- detail::AreaBase *> >::type * = 0);
- template <class Stream, class AreaClass, class Level> void route(
- action_t action = ACCEPT, int index = -1,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
- detail::AreaBase *> >::type * = 0);
-
- template <class Stream> void unroute(
- action_t action = ACCEPT);
- template <class Stream, class Level> void unroute(
- action_t action = ACCEPT,
- typename boost::enable_if< boost::is_convertible<Level*,
- detail::LevelBase *> >::type * = 0);
- template <class Stream, class Area> void unroute(
- action_t action = ACCEPT,
- typename boost::enable_if< boost::is_convertible<Area*,
- detail::AreaBase *> >::type * = 0);
- template <class Stream, class AreaClass> void unroute(
- action_t action = ACCEPT,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
- detail::AreaBase *> >::type * = 0);
- template <class Stream, class Area, class Level> void unroute(
- action_t action = ACCEPT,
- typename boost::enable_if< boost::is_convertible<Area*,
- detail::AreaBase *> >::type * = 0);
- template <class Stream, class AreaClass, class Level> void unroute(
- action_t action = ACCEPT,
- typename boost::enable_if< boost::is_convertible<typename AreaClass::SENFLogArea *,
- detail::AreaBase *> >::type * = 0);
+ template <class A1>
+ void route(action_t action = ACCEPT, int index = -1);
+ template <class A1, class A2>
+ void route(action_t action = ACCEPT, int index = -1);
+ template <class A1, class A2, class A3>
+ void route(action_t action = ACCEPT, int index = -1);
+
+ template <class A1>
+ void unroute(action_t action = ACCEPT);
+ template <class A1, class A2>
+ void unroute(action_t action = ACCEPT);
+ template <class A1, class A2, class A3>
+ void unroute(action_t action = ACCEPT);
# endif
template <class Stream, class Area, class Level>
void write(std::string msg);
+ senf::mpl::rv<0u> RouteParameterCheck_(...);
+ senf::mpl::rv<1u> RouteParameterCheck_(StreamBase *);
+ senf::mpl::rv<2u> RouteParameterCheck_(AreaBase *);
+ template <class T> senf::mpl::rv<3u> RouteParameterCheck_(T*, typename T::SENFLogArea * = 0);
+ senf::mpl::rv<4u> RouteParameterCheck_(LevelBase *);
+
+ template < class T, class A2, class A1,
+ unsigned type = SENF_MPL_RV( RouteParameterCheck_(static_cast<T*>(0)) ) >
+ struct RouteParameters
+ {};
+
+ template <class A2, class A1>
+ struct RouteParameters<mpl::nil,A2,A1,0u>
+ : public RouteParameters<A2,A1,mpl::nil>
+ {};
+
+ struct NilLevel {
+ static unsigned const value = NONE::value;
+ };
+
+ template <>
+ struct RouteParameters<mpl::nil,mpl::nil,mpl::nil,0u>
+ {
+ typedef mpl::nil Stream;
+ typedef mpl::nil Area;
+ typedef NilLevel Level;
+ };
+
+ template <class T, class A2, class A1>
+ struct RouteParameters<T,A2,A1,1u>
+ : public RouteParameters<A2,A1,mpl::nil>
+ {
+ typedef T Stream;
+ };
+
+ template <class T, class A2, class A1>
+ struct RouteParameters<T,A2,A1,2u>
+ : public RouteParameters<A2,A1,mpl::nil>
+ {
+ typedef T Area;
+ };
+
+ template <class T, class A2, class A1>
+ struct RouteParameters<T,A2,A1,3u>
+ : public RouteParameters<A2,A1,mpl::nil>
+ {
+ typedef typename T::SENFLogArea Area;
+ };
+
+ template <class T, class A2, class A1>
+ struct RouteParameters<T,A2,A1,4u>
+ : public RouteParameters<A2,A1,mpl::nil>
+ {
+ typedef T Level;
+ };
+
+ template <class T, class RV>
+ struct InstanceP
+ {
+ static RV * value() { return & T::instance(); }
+ };
+
+ template <class RV>
+ struct InstanceP<mpl::nil, RV>
+ {
+ static RV * value() { return 0; }
+ };
+
}}}
///////////////////////////////ih.e////////////////////////////////////////
href="http://www.stack.nl/~dimitri/doxygen/">Doxygen</a>. This infrastructure uses quite a bit
of pre- and postprocessing (which is integrated with the provided Doxygen builder) to fix some
doxygen problems and generate a (IMHO) more readable layout.
+
+ \autotoc
- \section senfutil Building Projects using SENF
+ \section senfutil_overview Building Projects using SENF
When building projects using senf, SENFSCons has a very simple helper module \ref senfutil to
make the building of libraries utilizing senf simpler:
LOGLEVELS='senf::log::Debug||IMPORTANT myapp::Transactions|mytrans::Area|VERBOSE'
</pre>
- \section layout The Project Layout
+ \section senfscons_intro Introduction to the SENFSCons build system
- A Project using the SENFSCons infrastructure will always use a consistent directory layout. The
- top-level directory will contain one subdirectory for every module. The main target will often
- be considered to be just another module using the facilities provided by library modules.
+ Here we give an overview on how SENF itself is built. The SENFSCons system aims to be quite
+ flexible at separates SENF specific tasks from generic tasks to facilitate reuse.
+
+ \subsection senfscons_layout The Project Layout
+
+ The SENFSCons infrastructure will always use a consistent directory layout. The top-level
+ directory will contain one subdirectory for every module. The main target will often be
+ considered to be just another module using the facilities provided by library modules.
The top-level project directory must contain the SENFSCons module in 'senfscons'.
cross-link the different module documentations. The unit-tests as well are run on a per-module
basis.
- \section Standard Build Configuration
+ \subsection senfscons_buildconf Standard Build Configuration
When the \c SConsctruct and \c SConscript files are build using the default SENFSCons helpers,
by default all libraries and binaries are built. Some additional targets are
opts.Add( BoolOption('final', 'Build final (optimized) build', False) )
opts.Update(env)
- print env.subst('$LOGLEVELS')
-
if env.subst('$LOGLEVELS'):
env.Append( expandLogOption=expandLogOption )
env.Append( CPPDEFINES = { 'SENF_LOG_CONF': '$expandLogOption' } )