Packets: Adjust SENF_PARSER_VARIANT implementation for better public/private support
g0dil [Thu, 12 Jun 2008 08:08:09 +0000 (08:08 +0000)]
Packets: Implement 'ids()' option for SENF_PARSER_VARIANT
Packets: Update DTCPPacket to use the new SENF_PARSER_VARIANT features
HowTows/NewPacket: Adjust for new SENF_PARSER_VARIANT

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

HowTos/NewPacket/Mainpage.dox
Packets/MPEGDVBBundle/DTCPPacket.hh
Packets/VariantParser.hh
Packets/VariantParser.ih
Packets/VariantParser.test.cc

index aa400f9..3894806 100644 (file)
     automatically or it may be calculated from other values (we'll see later how to do this).
 
     In all these cases we will want to disallow the user to directly change the value, while still
-    allowing to read the value. To do this, we mark \e value \e fields as read-only:
+    allowing to read the value. To do this, we can mark \e value \e fields as read-only:
 
     \code
     SENF_PARSER_BITFIELD_RO ( checksumPresent,  1, bool     );
     sub-parsers.
 
     In this case however, we still want to allow the user to change the field value, albeit not
-    directly. We will need to go through the collection parser, in this case the variant. Since the
-    variant syntax to change the currently active variant is not very comfortable, we provide some
-    helper functions:
+    directly. We will need to go through the collection parser, in this case the variant. 
+
+    The syntax for accessing a variant is quite cumbersome. Therefore we adjust the variant
+    definition to generate a more usable interface:
 
     \code
-    void enableChecksum()  const { optionalFields_().init<1>(); }
-    void disableChecksum() const { optionalFields_().init<0>(); }
+    SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
+                                      (novalue(disable_checksum, senf::VoidPacketParser))
+                                      (     id(checksum,         GREPacketParser_OptFields)) );
     \endcode
 
-    By changing the collection we automatically change the field, that this collection references.
-    Now we only need to provide an additional \a checksum member to hide the complex variant access
-    syntax from the user. And, since all variant access is now done via specialized members, we can
-    hide the variant field completely from the user:
-    
+    Here, we changed to things:
+    \li We made the variant private
+    \li We added some optional information to the variants type list
+
+    With this information, \ref SENF_PARSER_PRIVATE_VARIANT() will create some additional \e public
+    accessor members (those are public, only the variant itself is private). The members generated
+    work like:
     \code
-    SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
-                                      (senf::VoidPacketParser)
-                                      (GREPacketParser_OptFields) );
+    void disable_checksum() const { optionalFields_().init<0>; }
 
-    GREPacketParser_OptFields::checksum_t checksum() const
-        { return optionalFields_().get<1>().checksum(); }
+    typedef GREPacketParser_OptFields checksum_t;
+    checksum_t checksum()      const { return optionalFields_().get<1>(); }
+    void       init_checksum() const { optionalFields_().init<1>; }
+    bool       has_checksum()  const { return optionalFields_().variant() == 1; }
     \endcode
+    (Again: We don't implement these fields ourselves, this is done by \ref SENF_PARSER_VARIANT())
 
-    As we can see here, we have used the <em>name</em><code>_t</code> typedef which is available for
-    all fields to provide the return value for our accessor member.
+    \c disable_checksum() and \c init_checksum() change the selected variant. This will
+    automatically change the \c checksumPresent() field accordingly.
 
     The \c GREPacketParser is now simple and safe to use. The only responsibility of the user now is to
     only access \a checksum() if the \a checksumPresent() field is set. Otherwise, the behavior is
     \code
     #include <senf/Utils/IpChecksum.hh>
 
-    checksum_t::value_type calculateChecksum() const 
+    checksum_t::checksum_t::value_type calculateChecksum() const 
     {
         if (!checksumEnabled()) 
             return 0;
         SENF_PARSER_FIELD           ( protocolType,    senf::UInt16Parser            );
 
         SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
-                                                         (senf::VoidPacketParser)
-                                                         (GREPacketParser_OptFields) );
-
-        GREPacketParser_OptFields::checksum_t checksum() const
-            { return optionalFields_().get<1>().checksum(); }
+                                          (novalue(disable_checksum, senf::VoidPacketParser))
+                                          (     id(checksum,         GREPacketParser_OptFields)) );
 
-        void enableChecksum()  const { optionalFields_().init<1>(); }
-        void disableChecksum() const { optionalFields_().init<0>(); }
-    
         SENF_PARSER_FINALIZE(GREPacketParser);
 
-        checksum_t::value_type calculateChecksum() const;
+        checksum_t::checksum_t::value_type calculateChecksum() const;
     };
 
     // In the implementation (.cc) file:
 
         SENF_PARSER_FIELD            ( protocolType,    senf::UInt16Parser            );
 
-        SENF_PARSER_PRIVATE_VARIANT  ( optionalFields_, checksumPresent,
-                                                          (senf::VoidPacketParser)
-                                                          (GREPacketParser_OptFields) );
+        SENF_PARSER_PRIVATE_VARIANT ( optionalFields_, checksumPresent,
+                                          (novalue(disable_checksum, senf::VoidPacketParser))
+                                          (     id(checksum,         GREPacketParser_OptFields)) );
 
         bool valid() const { return version() == 0 && reserved0_5bits_() == 0; }
 
-        typedef GREPacketParser_OptFields::checksum_t checksum_t;
-        checksum_t checksum() const
-            { return optionalFields_().get<1>().checksum(); }
-
-        void enableChecksum()  const { optionalFields_().init<1>(); }
-        void disableChecksum() const { optionalFields_().init<0>(); }
-    
         SENF_PARSER_FINALIZE(GREPacketParser);
 
-        checksum_t::value_type calculateChecksum() const;
+        checksum_t::checksum_t::value_type calculateChecksum() const;
     };
 
     struct GREPacketType
     SENF_PACKET_REGISTRY_REGISTER( senf::EtherTypes, 0x6558, senf::EthernetPacket );
     SENF_PACKET_REGISTRY_REGISTER( senf::IpTypes, 47, GREPacket );
 
-    GREPacketParser::checksum_t::value_type GREPacketParser::calculateChecksum() const
+    GREPacketParser::checksum_t::checksum_t::value_type GREPacketParser::calculateChecksum() const
     {
         if (!checksumEnabled()) 
             return 0;
index 49601f9..5166c63 100644 (file)
@@ -86,41 +86,17 @@ namespace senf {
          *      -you NEVER can use templated Parsers in these macros since the macro-preprocessor won't recognize the <> brackets and will
          *      interpret the ","
          * 
-         * The first problem is solved by using (actually inventing)  SENF_PARSER_VARIANT_TRANS which has the same limitations 
-         *      concerning the number of types but isn't limited to the values used. This is achieved by a translating function 
-         *      as you can see. 
+         * The first problem is solved by using key()
          * The second problem is solved by introducing Helper-Parser which cover both the list and the number field. By that no 
          *      templates have to be used. 
          */
 
-        struct ip_version_translator {
-           typedef unsigned value_type;
-            static unsigned get(ip_version_t::value_type in) {
-                switch (in) { 
-                case 4: return 0;
-                case 6: return 1;
-                }
-                return 1; //default. should rather throw an exception 
-            }
-            static ip_version_t::value_type set(unsigned in) {
-                switch (in) {
-                case 0: return 4;
-                case 1: return 6; 
-                }
-                return 6; //default. should rather throw an exception 
-            }
-        };
-    
-        SENF_PARSER_VARIANT          ( fbiplist, transform(ip_version_translator, ip_version),
-                                                                 (senf::DTCPIPv4AddressListParser)        //IPv4 
-                                                                 (senf::DTCPIPv6AddressListParser) );     //IPv6
+        SENF_PARSER_VARIANT( fbiplist, ip_version,
+                                        ( ids(getIpv4AddressList, na, setIpVersion4, 
+                                      key(4, senf::DTCPIPv4AddressListParser)) )    //IPv4 
+                                ( ids(getIpv6AddressList, na, setIpVersion6,
+                                      key(6, senf::DTCPIPv6AddressListParser)) ) ); //IPv6
                                                                  
-        DTCPIPv4AddressListParser getIpv4AddressList () const { return fbiplist().get<0>(); }  // this is the absolute index 
-        DTCPIPv6AddressListParser getIpv6AddressList () const { return fbiplist().get<1>(); }
-
-        void setIpVersion4() const { fbiplist().init<0>(); }
-        void setIpVersion6() const { fbiplist().init<1>(); }
-
         SENF_PARSER_FINALIZE(DTCPPacketParser);
     };
     
index 8516a9f..8722e95 100644 (file)
@@ -125,26 +125,24 @@ namespace senf {
 
     /** \brief Define VariantParser field
 
-        This macro is a special helper to define a senf::DirectVariantParser type field. This is a
-        variant field which chooses the sub-type by directly taking the value of some other field.
-
+        This macro is a special helper to define a senf::VariantParser type field.
         \code
         struct SomeParser : public PacketParserBase
         {
         #   include SENF_PARSER()
         
             SENF_PARSER_PRIVATE_FIELD( type, senf::UInt8Parser );
-            SENF_PARSER_PRIVATE_VARIANT( content, type,
-                                            (novalue( disable, senf::VoidPacketParser ))
-                                            (     id( uint8,   senf::UInt8Parser      ))
-                                            (     id( uint16,  senf::UInt16Parser     ))
-                                            (     id( uint24,  senf::UInt24Parser     ))
-                                            (     id( uint32,  senf::UInt32Parser     )) );
+            SENF_PARSER_VARIANT( content, type,
+                                    (novalue( disable, senf::VoidPacketParser ))
+                                    (     id( uint8,   senf::UInt8Parser      ))
+                                    (     id( uint16,  senf::UInt16Parser     ))
+                                    (     id( uint24,  senf::UInt24Parser     ))
+                                    (     id( uint32,  senf::UInt32Parser     )) );
 
             SENF_PARSER_FINALIZE(SomeParser);
         };
-        \endcode
-
+        \endcode 
+            
         The variant \c content chooses one of the sub parsers depending on the \c type field. If \c
         type is 0, senf::VoidPacketParser is selected, if it is 1, senf::UInt8Parser and so on. 
 
@@ -157,25 +155,34 @@ namespace senf {
         <tr><td>\a type</td><td>Do not provide accessor and use the 0-based type index as
         key.</td></tr>
 
-        <tr><td>\c id(\a name, \a type)</td><td>Automatically create an accessor \a name for this
-        type</td></tr>
+        <tr><td>\c id(\a name, \a type)</td><td>Automatically create an accessor \a name, a test
+        function <tt>has_</tt>\a name and a function to switch to this type <tt>init_</tt>\a name
+        for this type. Identical to \c ids(\a name, <tt>has_</tt>\a name, <tt>init_</tt>\a name, \a
+        type)</td></tr>
 
         <tr><td>\c novalue(\a name, \a type)</td><td>This is like \c id but only provides an
-        initializer called \a name and no accessor</td></tr>
+        initializer called \a name and no accessor. Identical to \c ids(\c na, \c na, \a name, \a
+        type)</td></tr>
+
+        <tr><td>\c ids(\a accessorId, \a testId, \a initId, \a type)</td><td>This is again like \c
+        id but the names of the accessor, test function and init function are given
+        explicitly. Setting any of the id's to \c na will disable that function.</td></tr>
 
         <tr><td>\c key(\a value, \a type)</td><td>Use \a value to identity this type. The type is
         selected, when the \a chooser is equal to \a value</td></tr>
 
         <tr><td>\c id(\a name, \c key(\a value, \a type))<br/>\c novalue(\a name, \c key(\a value,
-        \a type))</td><td>The options may be nested in this way.</td></tr> </table>
+        \a type))</td><td>The options may be nested in this way.</td></tr>
+        </table>
+
+        Whenever an id is specified for any type, the variant itself will automatically be made
+        private so the only access to the variant from the outside is via the accessors.
 
-        It is customary, to hide the variant parser (by defining it private) and provide
-        more conveniently named accessors. In above example, these accessors are automatically
-        generated using the id's given. The exact members defined are:
+        The functions defined by specifying an id are:
 
         <table class="senf fixedcolumn">
-        <tr><td>\a name \c _t</td><td>The type for this specific variant value if not \c
-        novalue.</td></tr> 
+        <tr><td><em>name</em><tt>_t</tt></td><td>The type for this specific variant value if not \c
+        novalue.</td></tr>
 
         <tr><td><em>name</em><tt>_t</tt> <em>name</em>()</td><td>Return the variant value at this id
         if not \c novalue.</td></tr>
@@ -187,7 +194,7 @@ namespace senf {
         currently holds this kind of value, \c false otherwise. Only if not \c novalue.</td></tr>
         </table>
 
-        If \a value keys are given, they designate the value to expect in the \a chooser field to
+        If \c key specs are given, they designate the value to expect in the \a chooser field to
         select a specific variant type. If the \a chooser value does not match any key, the variant
         will be initialized to the \e first type.
 
@@ -226,7 +233,7 @@ namespace senf {
         \param[in] types a Boost.Preprocessor style sequence of sub-parser types
 
         \see 
-            senf::VariantParser \n 
+            senf::VariantParser for the VariantParser API\n 
             \ref SENF_PARSER_PRIVATE_VARIANT()
         \hideinitializer
         \ingroup packetparsermacros
index fa8acdc..79db6c7 100644 (file)
@@ -34,6 +34,8 @@
 #include <boost/preprocessor/seq/for_each.hpp>
 #include <boost/preprocessor/logical/or.hpp>
 #include <boost/preprocessor/seq/for_each_i.hpp>
+#include <boost/preprocessor/logical/not.hpp>
+#include <boost/preprocessor/expr_if.hpp>
 
 ///////////////////////////////ih.p////////////////////////////////////////
 
@@ -136,8 +138,11 @@ namespace detail {
             BOOST_PP_CAT(name, _traits);                                                          \
     public:                                                                                       \
         SENF_PARSER_COLLECTION_I(                                                                 \
-            access, name, chooser, BOOST_PP_CAT(name, _traits) );                                 \
-        BOOST_PP_SEQ_FOR_EACH_I(SENF_PARSER_VARIANT_ACCESSOR, name, types)
+            BOOST_PP_IIF( SENF_PARSER_VARIANT_NEEDACCESSORS(types), private, access),             \
+            name, chooser, BOOST_PP_CAT(name, _traits) );                                         \
+    access:                                                                                       \
+        BOOST_PP_SEQ_FOR_EACH_I(SENF_PARSER_VARIANT_ACCESSOR, name, types)                        \
+    public:
 
 #   define SENF_PARSER_VARIANT_MAKETRANSFORM(name, types)                                         \
         BOOST_PP_SEQ_FOR_EACH_I(SENF_PARSER_VARIANT_KEYVALUE, name, types)                        \
@@ -173,23 +178,32 @@ namespace detail {
 
 #   define SENF_PARSER_VARIANT_NOACCESSOR(name, i, elem)
 #   define SENF_PARSER_VARIANT_MAKEACCESSOR(name, i, elem)                                        \
-        BOOST_PP_IF( SENF_PARSER_VARIANT_HASVALUE(elem),                                          \
-                     SENF_PARSER_VARIANT_MAKEVACCESSOR,                                           \
-                     SENF_PARSER_VARIANT_MAKENVACCESSOR )(name, i, elem)
-
-#   define SENF_PARSER_VARIANT_MAKEVACCESSOR(name, i, elem)                                       \
-        typedef SENF_PARSER_VARIANT_GETTYPE(elem)                                                 \
-            BOOST_PP_CAT(SENF_PARSER_VARIANT_GETID(elem), _t);                                    \
-        SENF_PARSER_VARIANT_GETTYPE(elem) SENF_PARSER_VARIANT_GETID(elem)() const                 \
-        { return name().get<i>(); }                                                               \
-        void BOOST_PP_CAT(init_, SENF_PARSER_VARIANT_GETID(elem))() const                         \
-        { name().init<i>(); }                                                                     \
-        bool BOOST_PP_CAT(has_, SENF_PARSER_VARIANT_GETID(elem))() const                          \
-        { return name().variant() == i; }
-
-#   define SENF_PARSER_VARIANT_MAKENVACCESSOR(name, i, elem)                                      \
-        void SENF_PARSER_VARIANT_GETID(elem)() const                                              \
-        { name().init<i>(); }
+        SENF_PARSER_VARIANT_MAKEACCESSOR_VALUE(name, i, elem, SENF_PARSER_VARIANT_GETID(elem))    \
+        SENF_PARSER_VARIANT_MAKEACCESSOR_HAS(name, i, elem, SENF_PARSER_VARIANT_GETHASID(elem))   \
+        SENF_PARSER_VARIANT_MAKEACCESSOR_INIT(name, i, elem, SENF_PARSER_VARIANT_GETINITID(elem))
+
+#   define SENF_PARSER_VARIANT_IFNOTNA(id, x)                                                     \
+        BOOST_PP_EXPR_IIF( BOOST_PP_NOT( SENF_PARSER_VARIANT_NA(id) ), x )
+    
+#   define SENF_PARSER_VARIANT_MAKEACCESSOR_VALUE(name, i, elem, id)                              \
+        SENF_PARSER_VARIANT_IFNOTNA( id,                                                          \
+            typedef SENF_PARSER_VARIANT_GETTYPE(elem)                                             \
+                BOOST_PP_CAT(id, _t);                                                             \
+            BOOST_PP_CAT(id, _t) id() const                                                       \
+                { return name().get<i>(); }                                                       \
+        )
+                                     
+#   define SENF_PARSER_VARIANT_MAKEACCESSOR_HAS(name, i, elem, id)                                \
+        SENF_PARSER_VARIANT_IFNOTNA( id,                                                          \
+            bool id() const                                                                       \
+                { return name().variant() == i; }                                                 \
+        )
+
+#   define SENF_PARSER_VARIANT_MAKEACCESSOR_INIT(name, i, elem, id)                               \
+        SENF_PARSER_VARIANT_IFNOTNA( id,                                                          \
+            void id() const                                                                       \
+            { name().init<i>(); }                                                                 \
+        )
 
 #   define SENF_PARSER_VARIANT_KEY_GOBBLE__key(key, type)
 #   define SENF_PARSER_VARIANT_KEY_GETKEY__key(key, type) key
@@ -198,12 +212,25 @@ namespace detail {
 #   define SENF_PARSER_VARIANT_ID_GOBBLE__id(id, value)
 #   define SENF_PARSER_VARIANT_ID_GETID__id(id, value) id
 #   define SENF_PARSER_VARIANT_ID_GETVALUE__id(id, value) value
+#   define SENF_PARSER_VARIANT_ID_GETHASID__id(id, value) SENF_CAT_RECURS3(has_, id)
+#   define SENF_PARSER_VARIANT_ID_GETINITID__id(id, value) SENF_CAT_RECURS3(init_, id)
 
 #   define SENF_PARSER_VARIANT_ID_GOBBLE__novalue(id, value)
-#   define SENF_PARSER_VARIANT_ID_GETID__novalue(id, value) id
+#   define SENF_PARSER_VARIANT_ID_GETID__novalue(id, value) na
 #   define SENF_PARSER_VARIANT_ID_GETVALUE__novalue(id, value) value
+#   define SENF_PARSER_VARIANT_ID_GETHASID__novalue(id, value) na
+#   define SENF_PARSER_VARIANT_ID_GETINITID__novalue(id, value) id
+
+#   define SENF_PARSER_VARIANT_ID_GOBBLE__ids(id, hasid, initid, value)
+#   define SENF_PARSER_VARIANT_ID_GETID__ids(id, hasid, initid, value) id
+#   define SENF_PARSER_VARIANT_ID_GETVALUE__ids(id, hasid, initid, value) value
+#   define SENF_PARSER_VARIANT_ID_GETHASID__ids(id, hasid, initid, value) hasid
+#   define SENF_PARSER_VARIANT_ID_GETINITID__ids(id, hasid, initid, value) initid
 
-#   define SENF_PARSER_VARIANT_HASVALUE_GOBBLE__id(id, value)
+#   define SENF_PARSER_VARIANT_NA_GOBBLE__na
+
+#   define SENF_PARSER_VARIANT_NA(x)                                                              \
+        BOOST_PP_IS_EMPTY( SENF_CAT_RECURS1(SENF_PARSER_VARIANT_NA_GOBBLE__, x) )
 
 #   define SENF_PARSER_VARIANT_HASKEY(x)                                                          \
         SENF_PARSER_VARIANT_HASKEY_( SENF_PARSER_VARIANT_GETVALUE(x) )
@@ -225,14 +252,17 @@ namespace detail {
 #   define SENF_PARSER_VARIANT_GETID(x)                                                           \
         BOOST_PP_CAT(SENF_PARSER_VARIANT_ID_GETID__, x)
 
+#   define SENF_PARSER_VARIANT_GETHASID(x)                                                        \
+        BOOST_PP_CAT(SENF_PARSER_VARIANT_ID_GETHASID__, x)
+
+#   define SENF_PARSER_VARIANT_GETINITID(x)                                                       \
+        BOOST_PP_CAT(SENF_PARSER_VARIANT_ID_GETINITID__, x)
+
 #   define SENF_PARSER_VARIANT_GETVALUE(x)                                                        \
         BOOST_PP_IF( SENF_PARSER_VARIANT_HASID(x),                                                \
                      BOOST_PP_CAT(SENF_PARSER_VARIANT_ID_GETVALUE__, x),                          \
                      x )
 
-#   define SENF_PARSER_VARIANT_HASVALUE(x)                                                        \
-        BOOST_PP_IS_EMPTY( SENF_CAT_RECURS3(SENF_PARSER_VARIANT_HASVALUE_GOBBLE__, x) )
-
 #   define SENF_PARSER_VARIANT_GETTYPE(x)                                                         \
         SENF_PARSER_VARIANT_GETTYPE_( SENF_PARSER_VARIANT_GETVALUE(x) )
 
@@ -247,6 +277,12 @@ namespace detail {
 #   define SENF_PARSER_VARIANT_NEEDTRANSFORM_(s, state, elem)                                     \
         BOOST_PP_OR(state, SENF_PARSER_VARIANT_HASKEY(elem))
 
+#   define SENF_PARSER_VARIANT_NEEDACCESSORS(types)                                               \
+        BOOST_PP_SEQ_FOLD_LEFT(SENF_PARSER_VARIANT_NEEDACCESSORS_, 0, types)
+
+#   define SENF_PARSER_VARIANT_NEEDACCESSORS_(s, state, elem)                                     \
+        BOOST_PP_OR(state, SENF_PARSER_VARIANT_HASID(elem))
+
 #   define SENF_PARSER_VARIANT_TYPES(types)                                                       \
         BOOST_PP_SEQ_FOR_EACH(SENF_PARSER_VARIANT_TYPES_, _, types)
 
index d330e5a..e215a89 100644 (file)
@@ -96,8 +96,9 @@ namespace {
 
         SENF_PARSER_SKIP_BITS( 4 );
         SENF_PARSER_BITFIELD_RO( type, 4, unsigned );
-        SENF_PARSER_PRIVATE_VARIANT( content_, type,
-                                         ( novalue( nocontent, key(10, senf::VoidPacketParser)) )
+        SENF_PARSER_VARIANT( content_, type,
+                                         ( novalue( nocontent
+, key(10, senf::VoidPacketParser)) )
                                          (      id( content,           SubParser              ) )
             );