Some small documentation fixes
g0dil [Thu, 30 Aug 2007 23:27:30 +0000 (23:27 +0000)]
Utils: Implement mpl.hh metaprogramming helpers

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

Mainpage.dox
PPI/Connectors.hh
PPI/IOEvent.hh
Packets/Mainpage.dox
Packets/PacketParser.cti
Packets/PacketParser.ih
Socket/SocketPolicy.ih
Utils/Mainpage.dox
Utils/mpl.hh [new file with mode: 0644]
Utils/mpl.test.cc [new file with mode: 0644]

index 98afe2b..b0ec91a 100644 (file)
     \see \ref build \n
          \ref svnsetup
 
+    \section libPPI libPPI: Packet Processing Infrastructure
+
+    The Packet Processing Infrastructure implements a modular framework for implementing packet
+    oriented network applications. The library provides a larget set of pre-defined modules as well
+    as the necessary helpers to implement application specific processing modules.
+
+    \see <a href="../../PPI/doc/html/index.html">libPPI API reference</a>
+
     \section libSocket libSocket: C++ abstraction of the BSD socket API
 
     This library provides a high performance and object oriented abstraction of the standard socket
     API. It utilizes a flexible and extensible policy based design. The library provides predefined
-    types for the important socket types (UDP and TCP sockets etc) including raw and packet
-    sockets. \n
+    types for the important socket types (UDP and TCP sockets etc) including raw and packet sockets.
 
-    \see <a href="../../Socket/doc/html/index.html">libSocket API
-    reference</a>
+    \see <a href="../../Socket/doc/html/index.html">libSocket API reference</a>
 
     \section libPackets libPackets: Network packet manipulation
 
     packetized network data. Included is a library of several protocol parsers covering the basic
     IPv4 and IPv6 network protocols down to the Ethernet layer.
 
-    \see <a href="../../Packets/doc/html/index.html">libPackets API
-    reference</a>
+    \see <a href="../../Packets/doc/html/index.html">libPackets API reference</a>
 
     \section libScheduler libScheduler: Asynchronous event handling
 
index 9730338..fbaafb6 100644 (file)
@@ -307,9 +307,6 @@ namespace connector {
         OutputConnector();
     };
     
-    ///@{
-    ///\addtogroup connectors
-
     /** \brief Combination of PassiveConnector and InputConnector
 
         The PassiveInput automatically controls the connectors throttling state using a queueing
@@ -392,8 +389,6 @@ namespace connector {
         void connect(PassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
     };
 
-    ///@}
-
 }}}
 
 ///////////////////////////////hh.e////////////////////////////////////////
index ad239b8..8e18532 100644 (file)
@@ -52,6 +52,15 @@ namespace ppi {
         An IOEvent is signaled, whenever the FileHandle \a handle becomes readable or writable. The
         type of event is specified using the \a events mask with values from EventFlags.
 
+        There are two types of flags:
+        
+        \li <em>Event flags</em> (\ref Read, \ref Prio, \ref Write) specify the type of event. The
+            callback will be called whenever one of the specified events occurs on the filehandle
+        \li <em>Error flags</em> (\ref Hup, \ref Err) specify some type of error condition on the
+            filehandle. If you specify an error flag when registering the event, the error condition
+            will be passed to the callback, otherwise an ErrorException or HangupException will be
+            thrown.
+
         \see IOEventInfo
 
         \ingroup event_group
@@ -66,11 +75,13 @@ namespace ppi {
         // This is stupid, however there is no way to import the Scheduler::EventId enum together
         // with the enumeration symbols
 
-        enum EventFlags { Read  = Scheduler::EV_READ, 
-                          Prio = Scheduler::EV_PRIO,
-                          Write = Scheduler::EV_WRITE,
-                          Hup = Scheduler::EV_HUP,
-                          Err = Scheduler::EV_ERR };
+        enum EventFlags { 
+              Read  = Scheduler::EV_READ   /**< FileHandle is readable */
+            , Prio = Scheduler::EV_PRIO    /**< FileHandle priority data is readable */
+            , Write = Scheduler::EV_WRITE  /**< FileHandle is writable */
+            , Hup = Scheduler::EV_HUP      /**< Hangup condition on FileHandle */
+            , Err = Scheduler::EV_ERR      /**< Some other error condition on FileHandle */
+        };
 
         ///////////////////////////////////////////////////////////////////////////
         ///\name Structors and default members
index f541660..22a0f1b 100644 (file)
     udp->source()      = 2000u;
     udp->destination() = 2001u;
     ip->ttl()          = 255u;
-    ip->source()       = senf::INet4Address("192.168.0.1"); // (*)
-    ip->destination()  = senf::INet4Address("192.168.0.2"); // (*)
-    eth->source()      = senf::MACAddress("00:11:22:33:44:55");
-    eth->destination() = senf::MACAddress("00:11:22:33:44:66");
+    ip->source()       = senf::INet4Address::from_string("192.168.0.1");
+    ip->destination()  = senf::INet4Address::from_string("192.168.0.2");
+    eth->source()      = senf::MACAddress::from_string("00:11:22:33:44:55");
+    eth->destination() = senf::MACAddress::from_string("00:11:22:33:44:66");
     
-    eth.finalize(); // (*)
+    eth.finalize();
     \endcode
 
     As seen above, packet fields are accessed using the <tt>-></tt> operator whereas other packet
index 777531b..7413037 100644 (file)
@@ -77,7 +77,7 @@ prefix_ Parser senf::operator<<(Parser target, Value const & value)
 
 template <class Parser>
 prefix_ senf::PacketParserBase::size_type
-senf::detail::packetParserSize(Parser p, int, Parser_TakeNum<Parser::fixed_bytes> *)
+senf::detail::packetParserSize(Parser p, int, senf::mpl::take_uint<Parser::fixed_bytes> *)
 {
     return Parser::fixed_bytes;
 }
index 9acee0f..2fb098a 100644 (file)
@@ -25,6 +25,7 @@
 #define IH_PacketParser_ 1
 
 // Custom includes
+#include "Utils/mpl.hh"
 
 ///////////////////////////////ih.p////////////////////////////////////////
 
@@ -35,35 +36,41 @@ namespace detail {
 
 #   ifndef DOXYGEN
 
-    template <PacketParserBase::size_type i>
-    struct Parser_TakeNum {};
-
+    // Use SFINAE to check, if Parser has an integer-valued fixed_bytes member. If not,
+    // 'Parser_TakeNum<Parser::fixed_bytes>' fails and the overload is removed from the overload
+    // set. 
     template <class Parser>
     PacketParserBase::size_type packetParserSize(
-        Parser p, int, Parser_TakeNum<Parser::fixed_bytes> * = 0);
+        Parser p, int, senf::mpl::take_uint<Parser::fixed_bytes> * = 0);
 
+    // An ellipsis is always the worst match. A call 'packetParserSize(p,0) will prefer above
+    // overload if that is not disabled by SFINAE.
     template <class Parser>
     PacketParserBase::size_type packetParserSize(Parser p, ...);
 
-    template <unsigned n> struct ParserInitBytes_RV { char _[16][n]; };
-
+    // Same as above: This overload is only enabled, if Parser has an integer values 'init_bytes'
+    // member.
     template <class Parser>
-    ParserInitBytes_RV<1> ParserInitBytes_Choose_(Parser_TakeNum<Parser::init_bytes> *);
-    
+    senf::mpl::rv<0> ParserInitBytes_Choose_(senf::mpl::take_uint<Parser::init_bytes> *);
+
     template <class Parser>
-    ParserInitBytes_RV<2> ParserInitBytes_Choose_(...);
+    senf::mpl::rv<1> ParserInitBytes_Choose_(...);
 
-    template <class Parser, unsigned size>
+    // This version of ParserInitBytes_Choose uses 'Parser::init_bytes' to provide 'value' (via
+    // 'boost::integral_constant')
+    template <class Parser, unsigned _>
     struct ParserInitBytes_Choose 
         : public boost::integral_constant<PacketParserBase::size_type, Parser::init_bytes> {};
 
+    // If Parser::init_bytes is not defined, this specialization is chosen which instead uses
+    // 'Parser::fixed_bytes'
     template <class Parser>
-    struct ParserInitBytes_Choose<Parser, sizeof(ParserInitBytes_RV<2>)>
+    struct ParserInitBytes_Choose<Parser, 1>
         : public boost::integral_constant<PacketParserBase::size_type, Parser::fixed_bytes> {};
 
     template <class Parser>
     struct ParserInitBytes
-        : public ParserInitBytes_Choose<Parser, sizeof(ParserInitBytes_Choose_<Parser>(0))> {};
+        : public ParserInitBytes_Choose<Parser,SENF_MPL_RV(ParserInitBytes_Choose_<Parser>(0))> {};
 
 #   endif
 
index 6d41c69..0f70243 100644 (file)
@@ -50,6 +50,7 @@
 #include <boost/mpl/and.hpp>
 #include <boost/utility.hpp> // for enable_if
 
+#include "Utils/mpl.hh"
 #include "Utils/pool_alloc_mixin.hh"
 
 ///////////////////////////////ih.p////////////////////////////////////////
@@ -112,11 +113,7 @@ namespace impl {
 
     struct nil {};
 
-    template <int N>
-    struct SocketPolicy_rv
-    { int v[N+1]; };
-
-    template <class Base, class Policy, int N>
+    template <class Base, class Policy, int _>
     struct MakeSocketPolicy_merge
     {};
 
@@ -126,11 +123,11 @@ namespace impl {
 
 #    define BOOST_PP_LOCAL_LIMITS (0, BOOST_PP_DEC( SENF_SOCKET_POLICIES_N ) )
 #    define BOOST_PP_LOCAL_MACRO(n)                                                               \
-        SocketPolicy_rv<n> MakeSocketPolicy_merge_(                                               \
+        senf::mpl::rv<n> MakeSocketPolicy_merge_(                                                 \
             BOOST_PP_CAT( BOOST_PP_SEQ_ELEM( n, SENF_SOCKET_POLICIES ),Base)*);                   \
                                                                                                   \
         template <class Base, class Policy>                                                       \
-        struct MakeSocketPolicy_merge<Base,Policy,sizeof(SocketPolicy_rv<n>)>                     \
+        struct MakeSocketPolicy_merge<Base,Policy,n>                                              \
         {                                                                                         \
             typedef SocketPolicy<                                                                 \
                BOOST_PP_SEQ_FOR_EACH_I( SP_DeclareMakeSocketPolicy_merge_member,                  \
@@ -149,7 +146,7 @@ namespace impl {
         struct apply
             : MakeSocketPolicy_merge<Base,
                                      Policy,
-                                     sizeof(MakeSocketPolicy_merge_(static_cast<Policy*>(0)))>
+                                     SENF_MPL_RV(MakeSocketPolicy_merge_(static_cast<Policy*>(0)))>
         {};
 
         template <class Base>
@@ -170,13 +167,13 @@ namespace impl {
         typename Base::SomePolicy *
 
     template <class Base>
-    SocketPolicy_rv<1> SocketPolicy_checkcompat_(
+    senf::mpl::rv<1> SocketPolicy_checkcompat_(
         BOOST_PP_SEQ_FOR_EACH_I( SP_DeclareArguments, , SENF_SOCKET_POLICIES ) );
 
 #   undef SP_DeclareArguments
 
     template <class Base>
-    SocketPolicy_rv<2> SocketPolicy_checkcompat_( ... );
+    senf::mpl::rv<2> SocketPolicy_checkcompat_( ... );
 
     template <int Size>
     struct SocketPolicy_checkcompat
@@ -184,7 +181,7 @@ namespace impl {
     {};
 
     template<>
-    struct SocketPolicy_checkcompat<sizeof(SocketPolicy_rv<1>)>
+    struct SocketPolicy_checkcompat<1>
         : public boost::true_type
     {};
 
@@ -195,7 +192,7 @@ namespace impl {
 
     template <class Base, class Derived>
     struct SocketPolicy_compatibility
-        : public SocketPolicy_checkcompat< sizeof(
+        : public SocketPolicy_checkcompat< SENF_MPL_RV(
             SocketPolicy_checkcompat_<Base>(
                 BOOST_PP_SEQ_FOR_EACH_I( SP_DeclareArguments, , SENF_SOCKET_POLICIES ) )) >
     {};
index b0670e6..822c40b 100644 (file)
@@ -10,8 +10,6 @@ namespace senf {
 
     <dt>SystemException</dt><dd>standard exception for system errors (errno)</dd>
 
-    <dt>\ref time</dt><dd>Very rudimentary microsecond time support</dd>
-
     <dt>\ref process</dt><dd>Some simple process management and daemon helpers<?dd>
 
     <dt>\ref membind</dt><dd>a simple <a
@@ -39,8 +37,11 @@ namespace senf {
     <dt>\ref TypeIdValue</dt><dd>class wrapping a typeid in a way that it can be used like any other
     value type, e.g. as the key in a map.</dd>
     
-    <dt>\ref hexdump<dt>
-        <dd>a simple but usefull function to write binary data in in hexadecimal format.</dd>
+    <dt>\ref hexdump<dt><dd>a simple but usefull function to write binary data in in hexadecimal
+    format.</dd>
+
+    <dt>\ref senfmpl</dt><dd>Some simple tools which help to solve common meta-programming
+    tasks</dd>
 
     </dl>
  */
diff --git a/Utils/mpl.hh b/Utils/mpl.hh
new file mode 100644 (file)
index 0000000..2983ffe
--- /dev/null
@@ -0,0 +1,221 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+//     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 mpl public header */
+
+#ifndef HH_mpl_
+#define HH_mpl_ 1
+
+// Custom includes
+
+//#include "mpl.mpp"
+#include "mpl.ih"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace mpl {
+
+    /** \defgroup senfmpl Low-level template meta programming helpers
+     */
+
+#   ifndef SENF_MPL_RV_ALIGNMENT
+#       define SENF_MPL_RV_ALIGNMENT 16
+#   endif
+
+    /** \brief Return-value type used to implement overload selection
+
+        The senf::mpl::rv type is used together with \ref SENF_MPL_RV() to select template
+        specializations based on a set of overloads:
+
+        \code
+        template <unsigned _> struct select {};
+
+        // Case 0
+        template <>
+        struct select<0> {
+            static bool const has_int_value = true;
+            void frobble();
+        };
+        template <class T>
+        senf::mpl::rv<0> select_(int, senf::mpl::take_int<T::value> * = 0);
+
+        // Case 1
+        template <>
+        struct select<1> {
+            static bool const has_int_value = false;
+            void dazzle();
+        };
+        template <class T>
+        senf::mpl::rv<1> select_(...);
+
+        template <class T>
+        struct choice : public select<SENF_MPL_RV( select_<T>(0) )> {};
+
+        struct A { static const int value = 0; };
+        struct B {};
+
+        choice<A> a; a.frobble();
+        choice<B> b; b.dazzle();
+        \endcode
+
+        The selection is always based on two components: A selector class specialized for each of
+        the possible choices and an overloaded function (only signatures, no implementation needed)
+        to provide the conditions.
+
+        When instantiatinv <tt>choice<T></tt>, we forward \a T to the <tt>select_</tt> set of
+        overloads. Because of <a href="http://en.wikipedia.org/wiki/SFINAE">SFINAE</a>, the overload
+        set will only contain those instantiations, for which template expansion does not fail.
+
+        So, if \a T has an integer \c value member, both \c select_ overloads are ok and the call
+        <tt>select_<T>(0)</tt> will choose the first (case 0) variant, since the argument \c 0 is
+        better matched by \c int than by <tt>...</tt>.
+
+        However, if \a T does not have an integer \c value member, expansion for the first overload
+        fails and the overload set only contains the second case.
+
+        \ref SENF_MPL_RV() internally uses \c sizeof to find out, \e which overload was selected
+        and returns the senf::mpl::rv-argument of that overloads return type. For this to work, the
+        \c select_ functions need not be implemented since no code is generated and \c select_ is
+        never called.
+
+        This number is than forwarded as template argument to \c select which is specialized for
+        each case. Therefore, <tt>choice<A></tt> has a \c frobble() member whereas
+        <tt>choice<B></tt> has a \c dazzle() member.
+
+        \see \ref SENF_MPL_RV
+        \ingroup senfmpl
+     */
+    template <unsigned n>
+    struct rv { 
+        char _[SENF_MPL_RV_ALIGNMENT][n+1]; 
+    };
+
+    /** \brief Get return value of overload selector
+        
+        Used together with senf::mpl::rv to implement overload selection.
+        
+        \see \ref senf::mpl::rv
+        \ingroup senfmpl
+        \hideinitializer
+     */
+#   define SENF_MPL_RV(expr) (sizeof(expr)/SENF_MPL_RV_ALIGNMENT-1)
+
+    /** \brief Take an arbitrary unsigned integer template argument
+        
+        Used together with <a href="http://en.wikipedia.org/wiki/SFINAE">SFINAE</a>: The expression
+        <tt>take_uint<</tt> \a expr <tt>></tt> is only valid if \a expr is valid and returns a value
+        convertible to an unsigned integer.
+
+        \ingroup senfmpl
+     */
+    template <unsigned long _> struct take_uint {};
+
+    /** \brief Take an arbitrary integer template argument
+        
+        Used together with <a href="http://en.wikipedia.org/wiki/SFINAE">SFINAE</a>: The expression
+        <tt>take_int<</tt> \a expr <tt>></tt> is only valid if \a expr is valid and returns a value
+        convertible to an integer.
+
+        \ingroup senfmpl
+     */
+    template <long _> struct take_int {};
+
+    /** \brief Take an arbitrary type template argument
+        
+        Used together with <a href="http://en.wikipedia.org/wiki/SFINAE">SFINAE</a>: The expression
+        <tt>take_class<</tt> \a expr <tt>></tt> is only valid if \a expr is valid and is a type.
+
+        \ingroup senfmpl
+     */
+    template <class _> struct take_class {};
+
+    /** \brief Define 'previous value' slot
+
+        The 'previous value' macros \ref SENF_MPL_PREVV_DEF(), \ref SENF_MPL_PREVV_SET() and \ref
+        SENF_MPL_PREVV_GET() provide a facility to get the last unsigned integer value assigned to
+        the slot before the current point in the current class.
+        \code
+        struct Foo
+        {
+            // Define PREVV slot named 'accum' initialized to 0
+            SENF_MPL_PREVV_DEF(accum, 0);
+
+            // Add 2 to 'accum'
+            SENF_MPL_PREVV_SET(accum, SENF_MPL_PREVV_GET(accum) + 2);
+
+            // Multiply 'accum' by 3
+            SENF_MPL_PREVV_SET(accum, SENF_MPL_PREVV_GET(accum) * 3);
+
+            // Define the result as a constant expression. result is now 6
+            static unsigned result = SENF_MPL_PREVV_GET(accum);
+        };
+        \endcode
+        Of course, it does not make sense to use these macros for simple arithmetic as in the
+        example. The SENF_MPL_PREVV macros allow to define macros which pass information from one
+        macro invocation to the next.
+
+        \implementation The implementation is based on __LINE__: We check backwards for a value
+            defined on a previous line. The check is limited to 80 lines backwards.
+
+        \ingroup senfmpl
+        \hideinitializer
+     */
+#   define SENF_MPL_PREVV_DEF(name,value)                                                         \
+        template <unsigned _>                                                                     \
+        static senf::mpl::rv<0> _SENF_MPL_PREVV_ ## name (senf::mpl::rv<_> *);                    \
+        SENF_MPL_PREVV_SET(name,value)
+
+    /** \brief Set 'prevision value' slot
+        \see \ref SENF_MPL_PREVV_DEF()
+        \ingroup senfmpl
+        \hideinitializer
+     */
+#   define SENF_MPL_PREVV_SET(name,value)                                                         \
+        static senf::mpl::rv<unsigned(value)+1> _SENF_MPL_PREVV_ ## name (senf::mpl::rv<__LINE__>*)
+
+    /** \brief Get current 'previous value' slot value
+        \see \ref SENF_MPL_PREVV_DEF()
+        \ingroup senfmpl
+        \hideinitializer
+     */
+#   define SENF_MPL_PREVV_GET(name)                                                               \
+        SENF_MPL_PREVV_I_GET(name)
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "mpl.cci"
+//#include "mpl.ct"
+//#include "mpl.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:
diff --git a/Utils/mpl.test.cc b/Utils/mpl.test.cc
new file mode 100644 (file)
index 0000000..9935670
--- /dev/null
@@ -0,0 +1,97 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+//     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 mpl.test unit tests */
+
+//#include "mpl.test.hh"
+//#include "mpl.test.ih"
+
+// Custom includes
+#include "mpl.hh"
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+    
+    template <unsigned _> 
+    struct select {
+        static bool const has_int_value = false;
+        static bool const has_class_value = false;
+    };
+    template <class T>
+    senf::mpl::rv<0> select_(...);
+
+
+    template <>
+    struct select<1> {
+        static bool const has_int_value = true;
+        static bool const has_class_value = false;
+    };
+    template <class T>
+    senf::mpl::rv<1> select_(int, senf::mpl::take_int<T::value> * = 0);
+
+    template <>
+    struct select<2> {
+        static bool const has_int_value = false;
+        static bool const has_class_value = true;
+    };
+    template <class T>
+    senf::mpl::rv<2> select_(int, senf::mpl::take_class<typename T::value> * = 0);
+
+    template <class T> 
+    struct choice : public select<SENF_MPL_RV( select_<T>(0) )> {};
+
+    struct A { static const int value = 0; };
+    struct B { struct value {}; };
+    struct C {};
+}
+
+BOOST_AUTO_UNIT_TEST(senfmpl) 
+{
+    BOOST_CHECK( choice<A>::has_int_value );
+    BOOST_CHECK( ! choice<A>::has_class_value );
+
+    BOOST_CHECK( ! choice<B>::has_int_value );
+    BOOST_CHECK( choice<B>::has_class_value );
+
+    BOOST_CHECK( ! choice<C>::has_int_value );
+    BOOST_CHECK( ! choice<C>::has_class_value );
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\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: