Move Console from Scheduler into Utils
[senf.git] / Utils / Console / Node.hh
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Node public header */
25
26 /** \defgroup node_tree The node tree
27
28     The console/config node tree is the central data-structure of the library. Into this tree, all
29     commands and parameters are entered. The tree is then exposed using a file-system like
30     interface.
31
32     \autotoc
33
34     \section console_tree The tree
35
36     We will start by giving a more complete example. This example contains most of the stuff needed
37     for using the console/config library.
38
39     \code
40     // Define callback function.
41     void mycommand(std::ostream & os, senf::console::ParseCommandInfo const & command)
42     {
43         // ...
44         os << "!! Important message ...\n";
45     }
46
47     class SomeClass
48     {
49     public:
50         // Declare a directory node (proxy) for use by this class. This must be public so we can add
51         // it to the node tree later.
52         senf::console::ScopedDirectory<SomeClass> dir;
53
54         SomeClass() : dir(this)
55         {
56             // You may document the directory here or later when adding it to the tree
57             dir.doc("Manager for something");
58
59             // Add a member function (the pointer-to-member is automatically bound to this instance)
60             dir.add("member", &SomeClass::member)
61                 .doc("Do the member operation");
62         }
63
64         void member(std::ostream & os, senf::console::ParseCommandInfo const & command)
65         {
66             // ...
67         }
68     };
69
70     int main(int, char**)
71     {
72         // Provide global documentation
73         senf::console::root()
74             .doc("This is someServer server");
75
76         // Add a new directory to the root and document it. All the mutators return the node object
77         // itself so operations can be chained.
78         senf::console::DirectoryNode & mydir (
79                 .mkdir("myserver")
80                 .doc("My server specific directory"));
81
82         // Add a command to that directory
83         mydir.add("mycommand", &mycommand)
84             .doc("mycommand <foo> [<bar>]\n\n"
85                  "If <bar> is given, flurgle the <foo>, otherwise burgle it");
86
87         // Create a SomeClass instance and add it's directory.
88         SomeClass someClass;
89         mydir.add("someClass", someClass.dir);
90
91         // Start the interactive console server
92         senf::console::Server::start(senf::INet4SocketAddress(senf::INet4Address::None, 23232u))
93             .name("someServer");
94     }
95     \endcode
96
97     \subsection console_nodes Node types
98
99     The console/config library tree consists of two basic node types:
100
101     \li senf::console::DirectoryNode provides internal nodes with an arbitrary number of children
102     \li senf::console::CommandNode describes a command entry in the tree
103     \li senf::console::LinkNode is a link to another node. It works much like a symlink on POSIX
104         systems.
105
106     senf::console::CommandNode is the base-class of all command nodes of which there are several,
107     depending on the type of command.
108
109     There is a single root node, the senf::console::DirectoryNode called senf::console::root(). From
110     this node, the tree is traversed.
111
112     All nodes are allocated on the heap and are managed using a smart pointer.
113
114     \subsection console_manipulate Manipulating the node tree
115
116     There are several ways to add nodes to the tree:
117
118     \li A senf::console::DirectoryNode can be added using senf::console::DirectoryNode::mkdir().
119     \li An arbitrary node can be created and then (possibly later) added to the tree using the
120         corresponding senf::console::DirectoryNode::add() overload.
121     \li A senf::console::CommandNode is normally added to the tree by directly adding a callback
122         using one of the overloaded senf::console::DirectoryNode::add() members. See \ref
123         console_commands.
124     \li A senf::console::LinkNode is created by senf::console::DirectoryNode::link()
125
126     When directly adding a node callback, the type of node added depends on the type of
127     callback. The callback types which can be added are listed at \ref console_callbacks.
128
129     \code
130     void callback(std::ostream & os, senf::console::ParseCommandInfo const & command) { ... }
131     // ...
132     myDirectory.add("foo",&callback);
133     \endcode
134
135     Every node is identified among it's siblings by it's name. The name of the node is set when
136     adding the node to the tree. If the name is empty or non-unique, a unique name will be
137     automatically provided.
138
139     To remove a node from the tree, just use the nodes senf::console::GenericNode::unlink() or the
140     parents senf::console::DirectoryNode::remove() member. This call removes the node from it's
141     parent and returns a (smart) node pointer.
142
143     \li If you ignore the return value, the node (and it's children) will be deleted.
144     \li Alternatively, you may store away the node and re-attach it later.
145     \li An node (or subtree) can be moved to a different place by unlinking the node at it's old
146         place and re-adding it at it's new location.
147     \li To rename a node, unlink and re-add it with a different name.
148
149     \code
150     myDirectory.add("bar", myDirectory.remove("foo"));
151     \endcode
152
153     \subsection console_node_param Assigning additional node parameters
154
155     Depending on the node type added, additional node parameters may be set. For example, every node
156     has a documentation parameter which is used by the online-help system. To assign these
157     parameters, the node exposes corresponding member functions. Since
158     senf::console::DirectoryNode::add() returns the newly added node by reference, additional
159     parameters may just be added to the end of the add command:
160     \code
161     myDirectory.add("foo",&fooCallback).doc("The foo method");
162     \endcode
163     Since the parameter setters all return the node reference, additional parameters may just be
164     added to the end of the command.
165
166     \subsection console_tree_traverse Traversing the tree
167
168     The simplest way to access tree elements is to save the return value of the
169     senf::console::DirectoryNode::add() members. However, saving the reference will not ensure, that
170     the node is not removed. If the node might be removed from the tree, you should use a smart
171     pointer (either <tt>ptr</tt> or <tt>weak_ptr</tt>) to hold the node.
172
173     Another possibility is to traverse the tree explicitly. For this purpose, the operators '[]' and
174     '()' have been overloaded in senf::console::DirectoryNode.
175     \code
176     senf::console::root().getDirectory("myDirectory").getCommand("foo")
177     \\ or more concise but otherwise completely identical
178     senf::console::root()["myDirectory"]("foo")
179     \endcode
180
181     getDirectory and the '[]' operator will return a senf::console::DirectoryNode whereas getCommand
182     and the '()' operator will return a senf::console::CommandNode. If the node is not found or is
183     not of the correct type, an exception will be raised.
184
185     \section console_object_dir Assigning a directory to an object instance
186
187     Most objects will register several commands. So it makes sense for these objects to manage their
188     own directory. Since directories are however allocated on the heap, they cannot be directly
189     added to a class. To facilitate this usage, the senf::console::ScopedDirectory is used. This
190     class provides a senf::console::DirectoryNode facade. Internally, it automatically creates a
191     senf::console::DirectoryNode to which all calls are forwarded.
192
193     The senf::console::ScopedDirectory member should be declared public. This allows the user of the
194     class to add the node to the tree.
195  */
196
197 #ifndef HH_SENF_Scheduler_Console_Node_
198 #define HH_SENF_Scheduler_Console_Node_ 1
199
200 // Custom includes
201 #include <map>
202 #include <boost/shared_ptr.hpp>
203 #include <boost/weak_ptr.hpp>
204 #include <boost/enable_shared_from_this.hpp>
205 #include <boost/utility.hpp>
206 #include <boost/range/iterator_range.hpp>
207 #include <boost/typeof/typeof.hpp>
208 #include <boost/type_traits/remove_reference.hpp>
209 #include "../../Utils/Exception.hh"
210 #include "../../Utils/mpl.hh"
211 #include "../../Utils/Logger/SenfLog.hh"
212 #include "../../Utils/type_traits.hh"
213 #include "Parse.hh"
214
215 //#include "Node.mpp"
216 ///////////////////////////////hh.p////////////////////////////////////////
217
218 namespace senf {
219 namespace console {
220
221     class LinkNode;
222     class DirectoryNode;
223     class CommandNode;
224
225     DirectoryNode & root();
226
227     /** \brief Config/console node tree base-class
228
229         GenericNode is the base class of all node objects. There are two basic node types derived
230         from GenericNode:  DirectoryNode and CommandNode.
231
232         All nodes are dynamically allocated and automatically managed using reference counting.
233
234         All nodes are normally linked into a single tree which root node is
235         senf::console::root(). Nodes may however be orphaned (not linked to the tree) either
236         directly (the node has no parent) or indirectly (the node has a parent but is part of an
237         orphaned subtree which is not linked to the root node).
238
239         Every active (non-orphaned) node (except the root() node) has a non-empty node name. This
240         name is assigned to the node when adding the node to the tree.
241
242         \ingroup node_tree
243       */
244     class GenericNode
245         : public boost::enable_shared_from_this<GenericNode>
246     {
247         SENF_LOG_CLASS_AREA();
248     public:
249         ///////////////////////////////////////////////////////////////////////////
250         // Types
251
252         typedef boost::shared_ptr<GenericNode> ptr;
253         typedef boost::shared_ptr<GenericNode const> cptr;
254         typedef boost::weak_ptr<GenericNode> weak_ptr;
255
256         ///////////////////////////////////////////////////////////////////////////
257
258         virtual ~GenericNode();
259
260         std::string const & name() const; ///< Node name
261         boost::shared_ptr<DirectoryNode> parent() const; ///< Parent node
262                                         /**< May be null, if the node is the root node or if it is
263                                              not linked to the tree */
264
265         std::string path() const;       ///< Node path
266                                         /**< The node path is built by joining the names of all
267                                              parent nodes with '/' chars. */
268         std::string path(DirectoryNode const & root) const;
269                                         ///< Node path up to \a root
270                                         /**< The node path is built by joining the names of all
271                                              parent nodes up to \a root with '/' chars. */
272
273         ptr unlink();                   ///< Remove node from it's parent directory
274                                         /**< You may either discard the return value and thereby
275                                              dispose the node or may re-attach the node at some
276                                              other place using DirectoryNode::add(). */
277
278         bool active() const;            ///< \c true, if node is attached to the root() node
279
280         void help(std::ostream & output) const; /// Write help info to \a output
281
282         ptr thisptr();                  ///< Get smart pointer to node
283         cptr thisptr() const;           ///< Get smart pointer to node (const)
284
285         bool isChildOf(DirectoryNode & parent) const;
286                                         ///< \c true, if node is a child of \a parent
287                                         /**< Will also return \c true, if \a parent is the current
288                                              node. */
289
290         bool operator== (GenericNode & other) const;
291                                         ///< \c true, if this and \a other are the same node
292         bool operator!= (GenericNode & other) const;
293                                         ///< \c true, if this and \a other are different nodes
294
295         bool isDirectory() const;       ///< \c true, if this is a directory node
296         bool isLink() const;            ///< \c true, if this is a link node
297         bool isCommand() const;         ///< \c true, if this is a command node
298
299     protected:
300         GenericNode();
301
302         void name(std::string const & name);
303
304 #ifndef DOXYGEN
305     private:
306 #else
307     public:
308 #endif
309         virtual void v_help(std::ostream & output) const = 0;
310                                         ///< Provide help information
311                                         /**< This member must be implemented in derived classes
312                                              to provide node specific help information. */
313
314     private:
315         std::string name_;
316         DirectoryNode * parent_;
317
318         friend class DirectoryNode;
319     };
320
321     /** \brief Config/console tree link node
322
323         A LinkNode references another node and provides an additional alias name for that node. A
324         LinkNode works like a mixture of UNIX symlinks and hardlinks: It is an explicit link like a
325         UNIX symlink but references another node directly (not via it's path) like a UNIX
326         hardlink. Therefore, a LinkNode works across chroot().
327       */
328     class LinkNode
329         : public GenericNode
330     {
331     public:
332         ///////////////////////////////////////////////////////////////////////////
333         // Types
334
335         typedef boost::shared_ptr<LinkNode> ptr;
336         typedef boost::shared_ptr<LinkNode const> cptr;
337         typedef boost::weak_ptr<LinkNode> weak_ptr;
338
339         ///////////////////////////////////////////////////////////////////////////
340         ///\name Structors and default members
341         ///@{
342
343         static ptr create(GenericNode & node); ///< Create new link node.
344                                         /**< You should normally use DirectoryNode::link() to
345                                              create a link node. */
346
347         ///@}
348         ///////////////////////////////////////////////////////////////////////////
349
350         GenericNode & follow() const;   ///< Access the referenced node
351
352     protected:
353
354     private:
355         explicit LinkNode(GenericNode & node);
356
357         virtual void v_help(std::ostream &) const;
358
359         GenericNode::ptr node_;
360     };
361
362     class SimpleCommandNode;
363
364     /** \brief Internal: Node creation helper traits
365
366         This class is used internally to find out the type of node to create for a specific argument
367         type.
368      */
369     template <class Object>
370     struct NodeCreateTraits
371     {
372         typedef BOOST_TYPEOF_TPL( senf_console_add_node(
373                                       * static_cast<DirectoryNode *>(0),
374                                       * static_cast<std::string const *>(0),
375                                       * static_cast<Object *>(0),
376                                       0) ) base_type;
377         typedef typename senf::remove_cvref<base_type>::type value_type;
378
379         typedef typename value_type::node_type NodeType;
380         typedef typename value_type::return_type result_type;
381
382         /// Internal
383         struct Creator {
384             static result_type create(DirectoryNode & node, std::string const & name,
385                                       Object & ob);
386         };
387     };
388
389     /** \brief Config/console tree directory node
390
391         This node type provides the internal and root nodes of the tree. It allows to add arbitrary
392         children and supports directory traversal.
393
394         Nodes are normally not instantiated manually but are created by the DirectoryNode via
395         mkdir() or add(). Special add() members however allow externally allocated node objects.
396
397         Nodes may be added to the tree only once, otherwise chaos will ensue. Since nodes are always
398         managed dynamically, there is a special ScopedDirectory proxy template which provides a
399         DirectoryNode facade. ScopedDirectory is used if a class wants to manage it's own directory
400         as a data member.
401
402         Every node is assigned a (new) name when it is added to a directory. If the directory
403         already has an entry of that name, the name is made unique by appending a suffix of the form
404         '-n' where n is a number starting at 1. If the name is empty, int is set to 'unnamed' and
405         then uniquified as above. Automatically providing unique names simplifies adding
406         configuration/console support to generic components.
407
408         \ingroup node_tree
409       */
410     class DirectoryNode : public GenericNode
411     {
412         SENF_LOG_CLASS_AREA();
413         typedef std::map<std::string, GenericNode::ptr> ChildMap;
414     public:
415         ///////////////////////////////////////////////////////////////////////////
416         // Types
417
418         typedef boost::shared_ptr<DirectoryNode> ptr;
419         typedef boost::shared_ptr<DirectoryNode const> cptr;
420         typedef boost::weak_ptr<DirectoryNode> weak_ptr;
421
422         typedef boost::iterator_range<ChildMap::const_iterator> ChildrenRange;
423         typedef ChildMap::const_iterator child_iterator;
424
425         typedef DirectoryNode node_type;
426         typedef DirectoryNode & return_type;
427
428         ///////////////////////////////////////////////////////////////////////////
429         ///\name Structors and default members
430         ///\{
431
432         static ptr create();            ///< Create node object.
433                                         /**< You should normally use either mkdir() or
434                                              ScopedDirectory instead of create() */
435
436         ///\}
437         ///////////////////////////////////////////////////////////////////////////
438         ///\name Children
439         ///\{
440
441         template <class NodeType>
442         NodeType & add(std::string const & name, boost::shared_ptr<NodeType> node);
443                                         ///< Add node to tree
444                                         /**< Adds the \a node to the tree as a child of \a this
445                                              node. The node is given the name \a name. If a node of
446                                              that name already exists, a numeric suffix of the form
447                                              '-n' is added to the name until the name is unique. If
448                                              \a name is empty, it is set to 'unnamed'. */
449
450         template <class Object>
451         typename NodeCreateTraits<Object>::result_type add(std::string const & name,
452                                                            Object const & ob);
453                                         ///< Generic child node factory
454                                         /**< This member is used to create a new child node of the
455                                              current directory. The type of node created depends on
456                                              the type of argument passed.
457
458                                              The node type is selected by the NodeCreateTraits
459                                              class. To allow adding a specific node type, you need
460                                              to provide an overload for
461                                              <tt>senf_console_add_node</tt> which must be visible at
462                                              when you register the new node.
463                                              \code
464                                              MyNodeType & senf_console_add_node(
465                                                  DirectoryNode & dir,
466                                                  std::string const & name,
467                                                  MySpecialObject const & ob,
468                                                  int)
469                                              {
470                                                  return dir.add(name, MyNodeType::create(ob));
471                                              }
472                                              \endcode
473                                              (Do not forget the last unnamed 'int' parameter which
474                                              is not used but serves to disambiguate the
475                                              overloads). */
476
477         template <class Object>
478         typename NodeCreateTraits<Object>::result_type add(std::string const & name,
479                                                            Object & ob);
480                                         ///< Generic child node factory
481                                         /**< \see add() */
482
483         GenericNode::ptr remove(std::string const & name);
484                                         ///< Remove node \a name from the tree
485                                         /**< The returned pointer may either be discarded, which
486                                              will automatically dispose the removed node, or it may
487                                              be saved and/or re-attached at some other place in the
488                                              tree. */
489
490         bool hasChild(std::string const & name) const;
491                                         ///< \c true, if there is a child with name \a name
492
493         GenericNode & get(std::string const & name) const;
494                                         ///< Get child node
495                                         /**< \throws UnknownNodeNameException if a child \a name
496                                                  does not exist */
497         GenericNode & getLink(std::string const & name) const;
498                                         ///< Get child node without dereferencing links
499                                         /**< \throws UnknownNodeNameException if a child \a name
500                                                  does not exist */
501
502         DirectoryNode & getDirectory(std::string const & name) const;
503                                         ///< Get directory child node
504                                         /**< Same as operator[]
505                                              \throws UnknownNodeNameException if a child \a name
506                                                  does not exist.
507                                              \throws std::bad_cast if the child \a name is not a
508                                                  directory node. */
509
510         DirectoryNode & operator[](std::string const & name) const;
511                                         ///< Get directory child node
512                                         /**< Same as getDirectory
513                                              \throws UnknownNodeNameException if a child \a name
514                                                  does not exist.
515                                              \throws std::bad_cast if the child \a name is not a
516                                                  directory node. */
517
518         CommandNode & getCommand(std::string const & name) const;
519                                         ///< Get command child node
520                                         /**< Same as operator()
521                                              \throws UnknownNodeNameException if a child \a name
522                                                  does not exist
523                                              \throws std::bad_cast if the child \a name is not a
524                                                  command node. */
525
526         CommandNode & operator()(std::string const & name) const;
527                                         ///< Get command child node
528                                         /**< Same as getCommand()
529                                              \throws UnknownNodeNameException if a child \a name
530                                                  does not exist
531                                              \throws std::bad_cast if the child \a name is not a
532                                                  command node. */
533
534         DirectoryNode & mkdir(std::string const & name);
535                                         ///< Create sub-directory node
536
537         ChildrenRange children() const; ///< Return iterator range over all children.
538                                         /**< The returned range is sorted by child name. */
539
540         ChildrenRange completions(std::string const & s) const;
541                                         ///< Return iterator range of completions for \a s
542                                         /**< The returned range is sorted by child name. */
543
544         void link(std::string const & name, GenericNode & target);
545                                         ///< Create a child node which is a link to target. \a s
546                                         /**< The new link node will be a child of the node for which this member function is called. */
547
548         ///\}
549         ///////////////////////////////////////////////////////////////////////////
550
551         DirectoryNode & doc(std::string const & doc); ///< Set node documentation
552
553         ptr thisptr();
554         cptr thisptr() const;
555
556     protected:
557         DirectoryNode();
558
559     private:
560         void add(GenericNode::ptr node);
561         virtual void v_help(std::ostream & output) const;
562
563         ChildMap children_;
564         std::string doc_;
565
566         friend DirectoryNode & root();
567     };
568
569     /// Exception: Unknown node name
570     struct UnknownNodeNameException : public senf::Exception
571     { UnknownNodeNameException() : senf::Exception("Unknown node name") {}};
572
573 #ifndef DOXYGEN
574     template <class Type>
575     struct NodeCreateTraits< boost::shared_ptr<Type> >
576     {};
577 #endif
578
579     /** \brief Config/console tree command node
580
581         The CommandNode is the base-class for the tree leaf nodes. Concrete command node
582         implementations are derived from this class.
583
584         To execute a command, CommandNode::operator()() or CommandNode::execute() is called.
585
586         Subclass instances of this node type are automatically created when adding commands to the
587         tree. See \ref console_commands.
588
589         \ingroup node_tree
590       */
591     class CommandNode : public GenericNode
592     {
593         SENF_LOG_CLASS_AREA();
594     public:
595         ///////////////////////////////////////////////////////////////////////////
596         // Types
597
598         typedef boost::shared_ptr<CommandNode> ptr;
599         typedef boost::shared_ptr<CommandNode const> cptr;
600         typedef boost::weak_ptr<CommandNode> weak_ptr;
601
602         ///////////////////////////////////////////////////////////////////////////
603
604         void execute(std::ostream & output, ParseCommandInfo const & command) const;
605                                         ///< Execute the command
606                                         /**< Same as operator()()
607                                              \param[in] output stream where result messages may be
608                                                  written to
609                                              \param[in] arguments command arguments. This is a
610                                                  range of ranges of Token instances. */
611
612         void operator()(std::ostream & output, ParseCommandInfo const & command) const;
613                                         ///< Execute the command
614                                         /**< Same as execute()
615                                              \param[in] output stream where result messages may be
616                                                  written to
617                                              \param[in] arguments command arguments. This is a
618                                                  range of ranges of Token instances. */
619
620         ptr thisptr();
621         cptr thisptr() const;
622
623     protected:
624         CommandNode();
625
626 #ifndef DOXYGEN
627     private:
628 #endif
629         virtual void v_execute(std::ostream & output, ParseCommandInfo const & command) const = 0;
630                                         ///< Called to execute the command
631                                         /**< \param[in] output stream where result messages may be
632                                                  written to
633                                              \param[in] arguments command arguments. This is a
634                                                  range of ranges of Token instances. */
635
636     private:
637     };
638
639     /** \brief Most simple CommandNode implementation
640
641         This CommandNode implementation simply forwards the \a output and \a arguments arguments to
642         an arbitrary callback. Thus, it allows to add callbacks with the signature
643         \code
644         void callback(std::ostream & os, senf::console::ParseCommandInfo const & command)
645         { ... }
646         \endcode
647         to the tree.
648
649         \ingroup console_commands
650      */
651     class SimpleCommandNode : public CommandNode
652     {
653         SENF_LOG_CLASS_AREA();
654     public:
655         ///////////////////////////////////////////////////////////////////////////
656         // Types
657
658         typedef boost::shared_ptr<SimpleCommandNode> ptr;
659         typedef boost::shared_ptr<SimpleCommandNode const> cptr;
660         typedef boost::weak_ptr<SimpleCommandNode> weak_ptr;
661
662         typedef boost::function<void (std::ostream &, ParseCommandInfo const &)> Function;
663
664         typedef SimpleCommandNode node_type;
665         typedef SimpleCommandNode & return_type;
666
667         ///////////////////////////////////////////////////////////////////////////
668         ///\name Structors and default members
669         ///\{
670
671         static ptr create(Function const & fn);
672
673         ///\}
674         ///////////////////////////////////////////////////////////////////////////
675
676         ptr thisptr();
677         cptr thisptr() const;
678
679         SimpleCommandNode & doc(std::string const & doc);
680
681     protected:
682         SimpleCommandNode(Function const & fn);
683
684     private:
685         virtual void v_help(std::ostream & output) const;
686         virtual void v_execute(std::ostream & output, ParseCommandInfo const & command) const;
687
688
689         Function fn_;
690         std::string doc_;
691     };
692
693 #ifndef DOXYGEN
694
695     SimpleCommandNode & senf_console_add_node(DirectoryNode & node, std::string const & name,
696                                               SimpleCommandNode::Function fn, int);
697
698 #endif
699
700 }}
701
702 #include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
703
704 BOOST_TYPEOF_REGISTER_TYPE(senf::console::DirectoryNode)
705 BOOST_TYPEOF_REGISTER_TYPE(senf::console::SimpleCommandNode)
706
707
708 ///////////////////////////////hh.e////////////////////////////////////////
709 #include "Node.cci"
710 //#include "Node.ct"
711 #include "Node.cti"
712 #endif
713
714
715 // Local Variables:
716 // mode: c++
717 // fill-column: 100
718 // comment-column: 40
719 // c-file-style: "senf"
720 // indent-tabs-mode: nil
721 // ispell-local-dictionary: "american"
722 // compile-command: "scons -u test"
723 // End: