From: g0dil Date: Thu, 25 Jan 2007 10:34:00 +0000 (+0000) Subject: Further documentation (mainly SocketPolicy) X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=494e197e719a49fba726d90f668c2d96c02c744b;p=senf.git Further documentation (mainly SocketPolicy) git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@183 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Packets/ParseListS.hh b/Packets/ParseListS.hh index a480be6..87b7082 100644 --- a/Packets/ParseListS.hh +++ b/Packets/ParseListS.hh @@ -20,6 +20,36 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + + \idea Add an optional state to the sentinel and an optional + transition function. See ParseListS.hh for more. + + We should write a baseclass for sentinels which has no \c check() + member, en empty \c next() member and \c void as the state + type. This simplifies writing simple sentinels. + + The parse_listS iterator will have to pass the state in addition + to the current list element to \c check(). The \c next() member + will be invoked to advance the iterator. It is passer the current + element and a (non-const) reference to the state which it may + update. The Parse_ListS constructor must take an arbitrary number + of additional arguments which are forwarded to the state + initialization. + + This structure makes it simple to optimize away the overhead if + the state type is void. If we would always instantiate the + sentinel, this will always take up space. + + Another possibility would be to always instantiate the sentinel + and make the baseclass mandatory. The baseclass would then hold + the current raw iterator. The iterator itself would ONLY include a + single sentinel instance .. I think, this is the best solution, + sentinel members then have intrinsic access to the + state. Arguments are forwarded from the list constructor to the + Sentinel constructor. + */ + #ifndef HH_ParseListS_ #define HH_ParseListS_ 1 diff --git a/Socket/ClientSocketHandle.hh b/Socket/ClientSocketHandle.hh index 1a120b7..a1f3b92 100644 --- a/Socket/ClientSocketHandle.hh +++ b/Socket/ClientSocketHandle.hh @@ -31,8 +31,10 @@ ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { - - + + /// \addtogroup handle_group + /// @{ + template class ServerSocketHandle; /** \brief @@ -141,6 +143,7 @@ namespace senf { friend class senf::ServerSocketHandle; }; + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/FileHandle.hh b/Socket/FileHandle.hh index d2c93b6..5f82eaf 100644 --- a/Socket/FileHandle.hh +++ b/Socket/FileHandle.hh @@ -24,6 +24,46 @@ \brief senf::FileHandle public header */ +/** \defgroup handle_group The Handle Hierarchy + + \image html FhHierarchy.png + + The senf::FileHandle class is the base of a hierarchy of socket + handle classes (realized as templates). These classes provide an + interface to the complete socket API. While going down the + inheritance hierarchy, the interface will be more and more + complete. + + The most complete interface is provided by + senf::ProtocolClientSocketHandle and + senf::ProtocolServerSocketHandle. The template Arguments specifies + the Protocol class of the underlying socket type. These are the + \e only classes having public constructors and are therefore the + only classes, which may be created by the library user. You will + normally use these classes by naming a specific socket typedef + (e.g. senf::TCPv4ClientSocketHandle). + + However, to aid writing flexible and generic code, the socket + library provides the senf::ClientSocketHandle and + senf::ServerSocketHandle class templates. These templates + implement a family of closely related classes based on the + specification of the socket policy. This policy specification may + be \e incomplete (see below). Instances of + senf::ClientSocketHandle/senf::ServerSocketHandle can be assigned + and converted to different ClientSocketHandle/ServerSocketHandle + types as long as the policy specifications are compatible. + + \attention It is very important, to (almost) always pass the socket + handle by value. The socket handle is a very lightweight + class and designed to be used like an ordinary built-in type. This + is very important in combination with the policy interface. + + \note The FileHandle hierarchy below the SocketHandle template is + \e not meant to be user extensible. To add new socket types, you + should introduce new protocol and/or policy classes, the + SocketHandle classes should not be changed. + */ + #ifndef HH_FileHandle_ #define HH_FileHandle_ 1 @@ -36,7 +76,9 @@ #include "FileHandle.ih" namespace senf { - + + /// \addtogroup handle_group + /// @{ /** \brief Basic file handle wrapper @@ -151,8 +193,18 @@ namespace senf { FileBody::ptr body_; }; + /** \brief Adapt FileHandle to senf::Scheduler + \related senf::FileHandle + + \internal + + This function will be called by the Scheduler to retrieve the file descriptor of the + FileHandle. + */ int retrieve_filehandle(FileHandle handle); + /// @} + } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/Mainpage.dox b/Socket/Mainpage.dox index 2721d36..a7279fc 100644 --- a/Socket/Mainpage.dox +++ b/Socket/Mainpage.dox @@ -4,12 +4,13 @@ abstraction of the BSD socket API. The abstraction is based on several concepts: - \li The basic visible interface is a handle object - (senf::FileHandle and it's derived classes) - \li The socket interface relies on a policy framework to configure - it's functionality + \li The basic visible interface is a \link handle_group handle + object \endlink + \li The socket interface relies on a \link policy_group policy + framework \endlink to configure it's functionality \li The rest of the socket API is accessible using a classic - inheritance hierarchy of protocol classes + inheritance hierarchy of \link protocol_group protocol classes + \endlink The handle/body architecture provides automatic reference counted management of socket instances, the policy framework provides @@ -19,136 +20,65 @@ dependent options. \see \ref usage \n - \ref extend \n + \ref handle_group \n + \ref policy_group \n + \ref protocol_group \n + \ref extend \n \ref implementation */ /** \page usage Using the Socket Library - \section socket_handle The socket handle - Whenever you use the socket library, what you will be dealing with are senf::FileHandle derived instances. The socket library relies on reference counting to automatically manage the underlying socket representation. This frees you of having to manage the socket lifetime explicitly. - - \section socket_hierarchy The FileHandle hierarchy - - \image html FhHierarchy.png - - The senf::FileHandle class is the base of a hierarchy of socket - handle classes (realized as templates). These classes provide an - interface to the complete socket API. While going down the - inheritance hierarchy, the interface will be more and more - complete. - - The most complete interface is provided by - senf::ProtocolClientSocketHandle and - senf::ProtocolServerSocketHandle. The template Arguments specifies - the Protocol class of the underlying socket type. These are the - \e only classes having public constructors and are therefore the - only classes, which may be created by the library user. You will - normally use these classes by naming a specific socket typedef - (e.g. senf::TCPv4ClientSocketHandle). - - However, to aid writing flexible and generic code, the socket - library provides the senf::ClientSocketHandle and - senf::ServerSocketHandle class templates. These templates - implement a family of closely related classes based on the - specification of the socket policy. This policy specification may - be \e incomplete (see below). Instances of - senf::ClientSocketHandle/senf::ServerSocketHandle can be assigned - and converted to different ClientSocketHandle/ServerSocketHandle - types as long as the policy specifications are compatible. - \attention It is very important, to (almost) always pass the socket - handle by value. The socket handle is a very lightweight - class and designed to be used like an ordinary built-in type. This - is very important in combination with the policy interface. - - \section policy_framework The policy framework - - \image html SocketPolicy.png - - The policy framework conceptually implements a list of parallel - inheritance hierarchies each covering a specific interface aspect - of the socket handle. The socket handle itself only provides - minimal functionality. All further functionality is relayed to a - policy class, or more precisely, to a group of policy classes, one - for each policy axis. The policy axis are - -
-
addressingPolicy
-
configures, whether a socket is - addressable and if so, configures the address type
- -
framingPolicy
-
configures the type of framing the socket provides: either no - framing providing a simple i/o stream or packet framing
- -
communicationPolicy
-
configures,if and how the communication partner is - selected
- -
readPolicy
-
configures the readability of the socket
- -
writePolicy
-
configures the writability of the socket
+ \section usage_create Creating a Socket Handle + + To create a new socket handle (opening a socket), you will need to + use senf::ProtocolClientSocketHandle or + senf::ProtocolServerSocketHandle. You will probably not use these + templates as is but use proper typedefs (for example + senf::TCPv4ClientSocketHandle or senf::PacketSocketHandle). The + documentation for these socket handles are found in the protocol + class (for example senf::TCPv4SocketProtocol or + senf::PacketProtocol). + + \section usage_reusable Writing Reusable Components + + To make your code more flexible, you should not pass around your + socket in this form. Most of your code will be using only a small + subset of the senf::ProtocolClientSocketHandle or + senf::ProtocolServerSocketHandle API. If instead of using the + fully specified handle type you use a more incomplete type, you + allow your code to be used with all socket which fulfill the + minimal requirements of your code. + + This works, by defining a special reduced policy or handle for + your code: + + \code + typedef senf::ClientSocketHandle< + senf::MakeSocketPolicy< + senf::ReadablePolicy, + senf::StreamFramingPolicy, + senf::ConnectedCommunicationPolicy > > MyReadableHandle; + + \endcode + + This defines \c MyReadableHandle as a senf::ClientSocketHandle + which will have only read functionality. Your code expects a + stream interface (in contrast to a packet or datagram based + interface). You will not have \c write or \c readfrom members. \c + write will be disabled since the WritePolicy is unknown, \c + readfrom will be disabled since a socket with the + senf::ConnectedCommunicationPolicy does not have a \c readfrom + member. + */ -
bufferingPolicy
-
configures, if and how buffering is configured for a socket
-
- - Every Policy value is identified by a class type. The policy types - themselves built an inheritance hierarchy for each policy - axis. For each policy axis, the root of this tree is the class - named '(axis name)Base' (e.g. \p FramingPolicyBase or \p - CommunicationPolicyBase) which is aliased to 'Unspecified(axis - name)' (e.g. \p UnspecifiedFramingPolicy or \p - UnspecifiedCommunicationPolicy). - - The senf::SocketPolicy template combines a set of policy classes, - one for each policy axis. Together, they define the behavior of a - socket handle. The socket handle instances do net implement any - socket functionality themselves, instead defering the - implementation to the socket classes. The interface is therefore - \e not implemented using virtual members, all important socket - functions can be inlined by the compiler to create highly - efficient code. - - Two SocketPolicy instances are considered compatible, if all - policy axis are compatible. A policy axis is compatible, if one - policy class is either the same as the other or derived from - it. Two SocketHandle instances can be converted into each other, - as long as the SocketPolicies are compatible. - \section protocol_interface The protocol interface - - \image html Protocols.png - - The socket handle classes and templates only implement the most - important socket API methods using the policy framework. To access - the complete API, the protocol interface is provided. Access to - the protocol interface is only possible via - senf::ProtocolClientSocketHandle and - senf::ProtocolServerSocketHandle which have the necessary \c - protocol() member. This member returns a reference to the protocol - class instance which contains members covering all the API - function (mostly setsockopt/getsockopt related calls) not found in - the SocketHandle interface. The protocol interface is specific to - the protocol. It's implementation is quite free. The standard - protocols are implemented using a simple multiple-inheritance - hierarchy as shown above. - - Since the protocol class is protocol specific (how intelligent - ...), the protocol class also defines the complete socket policy - to be used with it's protocol. Complete meaning, that every policy - axis must be assigned it's the most specific (that is derived) - policy class to be used with the protocol. - - */ /** \page extend Extending the Library @@ -157,7 +87,7 @@ the protocol layer is quite simple and works as long as the desired protocol does use the same BSD API used by the standard internet protocols as implemented in the standard policies - (i.e. it uses ordinere read() and write() or rcvfrom() or sendto() + (i.e. it uses ordinary read() and write() or rcvfrom() or sendto() calls and so on). If however the implementation of a policy feature needs to be @@ -241,6 +171,8 @@ members additionally depend on other policy axis (example: AddressingPolicy::connect is only defined if the communication policy is ConnectedCommunication). + + \see policy_group */ /** \page implementation Implementation notes diff --git a/Socket/ProtocolClientSocketHandle.hh b/Socket/ProtocolClientSocketHandle.hh index cf23044..54a32d0 100644 --- a/Socket/ProtocolClientSocketHandle.hh +++ b/Socket/ProtocolClientSocketHandle.hh @@ -31,6 +31,8 @@ namespace senf { + /// \addtogroup handle_group + /// @{ template class ProtocolServerSocketHandle; @@ -73,6 +75,7 @@ namespace senf { friend class ProtocolServerSocketHandle; }; + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/ProtocolServerSocketHandle.hh b/Socket/ProtocolServerSocketHandle.hh index 2686510..002e209 100644 --- a/Socket/ProtocolServerSocketHandle.hh +++ b/Socket/ProtocolServerSocketHandle.hh @@ -30,8 +30,10 @@ ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { - - + + /// \addtogroup handle_group + /// @{ + template class ProtocolClientSocketHandle; /** \brief @@ -75,6 +77,7 @@ namespace senf { }; + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/ServerSocketHandle.hh b/Socket/ServerSocketHandle.hh index a626360..d1032b4 100644 --- a/Socket/ServerSocketHandle.hh +++ b/Socket/ServerSocketHandle.hh @@ -35,6 +35,8 @@ namespace senf { + /// \addtogroup handle_group + /// @{ template class ClientSocketHandle; @@ -110,6 +112,7 @@ namespace senf { }; + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/SocketHandle.cti b/Socket/SocketHandle.cti index e93ec63..08a3e4d 100644 --- a/Socket/SocketHandle.cti +++ b/Socket/SocketHandle.cti @@ -106,7 +106,7 @@ senf::SocketHandle::cast_dynamic(FileHandle handle) { // throws bad_cast if the body is not a SocketBody SocketBody & body (dynamic_cast(FileHandle::body(handle))); - // throws bad_cast if the poplicy is not compatible + // throws bad_cast if the policy is not compatible SocketPolicy::checkBaseOf(body.protocol().policy()); return cast_static(handle); } @@ -160,6 +160,11 @@ prefix_ bool senf::check_socket_cast(Source handle) template prefix_ void senf::SocketHandle::state(SocketStateMap & map, unsigned lod) { + // We use typeid here even though the type of *this is static + // (SocketHandle is not polymorphic and has no vtable). This will + // automatically include the SocketPolicy template parameter in + // the type name and therefore show the \e static policy of the + // socket handle. map["handle"] = prettyName(typeid(*this)); body().state(map,lod); } diff --git a/Socket/SocketHandle.hh b/Socket/SocketHandle.hh index 8f10b4a..25225c7 100644 --- a/Socket/SocketHandle.hh +++ b/Socket/SocketHandle.hh @@ -39,6 +39,8 @@ namespace senf { + /// \addtogroup handle_group + /// @{ /** \brief basic SocketHandle supporting protocol and policy abstraction @@ -121,11 +123,20 @@ namespace senf { \param map string to string mapping to be filled with state information \param lod level of detail requesten. The interpretation - of this value is protocol specific */ + of this value is protocol specific + + \implementation This member will be re-implemented in + every derived class. This is very important since state() + is \e not a virtual function (which we don't want since + we don't want to add a vtable pointer to every handle + instance). */ std::string dumpState(unsigned lod=0); ///< Format complete state information as string /**< Formats the complete state map value and returns it as - a single multi-line string. */ + a single multi-line string. + + \implementation This member will be re-implemented in + every derived class. See the state() documentation. */ protected: explicit SocketHandle(std::auto_ptr protocol, bool isServer); @@ -225,6 +236,8 @@ namespace senf { */ template bool check_socket_cast(Source handle); + + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/SocketHandle.ih b/Socket/SocketHandle.ih index c72b54d..4c52c65 100644 --- a/Socket/SocketHandle.ih +++ b/Socket/SocketHandle.ih @@ -153,10 +153,6 @@ namespace senf { otherwise */ void state(SocketStateMap & map, unsigned lod); - /**< \todo Move state into a virtual body member (which is - ok, since SocketBody already has virtual members). This - makes in unnecessary to reimplement dumpState and state - in every SocketHandle derived class */ private: virtual void v_close(); ///< Close socket diff --git a/Socket/SocketHandle.test.cc b/Socket/SocketHandle.test.cc index 8a3f6cd..a8a50cc 100644 --- a/Socket/SocketHandle.test.cc +++ b/Socket/SocketHandle.test.cc @@ -94,7 +94,6 @@ BOOST_AUTO_UNIT_TEST(socketHandle) "socket.policy: senf::SocketPolicy\n" "socket.protocol: senf::test::SomeProtocol\n" "socket.server: false\n" ); - } ///////////////////////////////cc.e//////////////////////////////////////// diff --git a/Socket/SocketPolicy.ct b/Socket/SocketPolicy.ct index cbcfd30..3f07f25 100644 --- a/Socket/SocketPolicy.ct +++ b/Socket/SocketPolicy.ct @@ -36,6 +36,9 @@ template < BOOST_PP_SEQ_FOR_EACH_I( SP_TemplateArgs, , SENF_SOCKET_POLICIES ) > prefix_ void senf::SocketPolicy< BOOST_PP_SEQ_FOR_EACH_I( SP_TemplateParams, , SENF_SOCKET_POLICIES ) >:: checkBaseOf(SocketPolicyBase const & other) { + // check, wether each policy of other is (dynamically!) convertible + // to the corresponding (static) policy of this class. Throws + // std::bad_cast on failure # define SP_CheckPolicy(x1,x2,SomePolicy) (void) dynamic_cast(other.BOOST_PP_CAT(the,SomePolicy)()); BOOST_PP_SEQ_FOR_EACH( SP_CheckPolicy, , SENF_SOCKET_POLICIES ) # undef SP_CheckPolicy diff --git a/Socket/SocketPolicy.hh b/Socket/SocketPolicy.hh index 91ee1bf..297a469 100644 --- a/Socket/SocketPolicy.hh +++ b/Socket/SocketPolicy.hh @@ -12,7 +12,7 @@ // // 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 +// 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 @@ -20,29 +20,380 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + \brief SocketPolicy public header + + \todo We should probably remove BufferingPolicy from the + interface, it does not make much sense (how did I come to include + it ??) + + \todo Do we want to support separate read and write policies. This + allows to treat pipes within this framework however, is this worth + the effort? + */ + +/** \defgroup policy_group The Policy Framework + + \image html SocketPolicy.png + + \section policy_group_introduction Introduction to the Policy Framework + + The policy framework conceptually implements a list of parallel + inheritance hierarchies each covering a specific interface aspect + of the socket handle. The socket handle itself only provides + minimal functionality. All further functionality is relayed to a + policy class, or more precisely, to a group of policy classes, one + for each policy axis. The policy axis are + +
+
addressingPolicy
+
configures, whether a socket is + addressable and if so, configures the address type
+ +
framingPolicy
+
configures the type of framing the socket provides: either no + framing providing a simple i/o stream or packet framing
+ +
communicationPolicy
+
configures,if and how the communication partner is + selected
+ +
readPolicy
+
configures the readability of the socket
+ +
writePolicy
+
configures the writability of the socket
+ +
bufferingPolicy
+
configures, if and how buffering is configured for a socket
+
+ + Every Policy value is identified by a class type. The policy + classes themselves built an inheritance hierarchy for each policy + axis. For each policy axis, the root of this tree is the class + named \i Policy \c Base (e.g. \p AddressingPolicyBase). + + The senf::SocketPolicy defines the complete policy of a socket. It + combines a set of policy classes, one for each policy + axis. Together, they define the behavior of a socket handle. The + socket handle instances do not implement any socket functionality + themselves instead defering the implementation to the policy + classes. The SocketHandle interface is therefore \e not + implemented using virtual members, all important socket functions + can be inlined by the compiler to create highly efficient code. + + A SocketPolicy can be incomplete. In this case it does \e not + completely specify the socket interface, it leaves some aspects + open. A SocketHandle based on such a policy will have a reduced + interface: It will only support those members for wich the + corresponding policies are defined. + + Two SocketHandle's with different policies can be \e + compatible. If they are, the more derived SocketHandle can be + converted (assigned to) the more basic SocketHandle. + + \section policy_group_details The Policy Framework Classes + + In the following discussion we will use the following conventions: + \li \e Policy is one or \c AddressingPolicy, \c FramingPolicy, \c + CommunicationPolicy, \c ReadPolicy, \c WritePolicy or \c + BufferingPolicy + \li \e socketPolicy is any socket policy (that is, an + instantiation of the SocketPolicy template) + \li \e trait is an any policy class (that is, any class derived + from one of the axis base classes) + + Each axis is comprised of a number of classes and templates (all + in namespace senf of course): + +
+
\e Policy \c Base (ex: AddressingPolicyBase)
+
Baseclass of all policies in this axis
+ +
\c Unspecified \e Policy (ex: \ref UnspecifiedAddressingPolicy)
+
An alias (typedef) for \e Policy \c Base
+ +
\e Policy \c Is < \e socketPolicy, \e trait > (ex: AddressingPolicyIs)
+
A template metafunction returning \c boost::true_type, if \e + trait (any class derived from \e Policy \c Base) is a compatible + policy value of the given \e socketPolicy
+ +
\c If \e Policy \c Is < \e socketPolicy, \e trait > (ex: IfAddressingPolicyIs)
+
This is a combination of \e Policy \c Is and \c boost::enable_if
+ +
\c If \e Policy \c IsNot < \e socketPolicy, \e trait > (ex: IfAddressingPolicyIsNot)
+
The inverse of above
+
+ + These classes form the basis of the policy framework. To bind the + policy axis together, there are some more classes and templates. + +
+
\c class \c SocketPolicyBase
+
This class is the base class of the SocketPolicy template. It + is used to validate, that a class is really a SocketPolicy (by + checking, that it derives from SocketPolicyBase. This is simpler + than chacking the template directly).
+ +
\c template \c SocketPolicy < \e addressingPolicy, \e framingPolicy, \e communicationPolicy, \e readPolicy, \e writePolicy, \e bufferingPolicy >
+
This is the central SocketPolicy template. It combines a + complete set of policy classes, one for each axis.
+ +
\c template \c MakeSocketPolicy < \e args >
+
\c MakeSocketPolicy is a template metafunction which + simplifies building SocketPolicy instantiations. It takes any + number (ok, up to a maximum of 6) of policy classes as an + argument (in any order). It will sort these arguments into the + SocketPolicy template arguments. If for some axis no class is + specified, it's slot will be filled with \c Unspecified \e + Policy. Additionally, the first Argument may optionally be ab + arbitrary SocketPolicy. It will provide default values for + unspecified axis
+ +
\c template \c SocketPolicyIsBaseOf < \e base, \e derived >
+
This template metafunction will check, wether the socket + policy \e derived is convertible to \e base. This means, that for + each axis, the corresponding policy class in \e derived must be + derived or be the same as the one on \e base.
+
+ + \implementation All these classes are created automatically. The + \c SENF_SOCKET_POLICIES makro is a Boost.Preprocessor style + sequence listing all policy axis. The Boost.Preprocessor library + is then used to generate the respective classes. + + \section policy_implement Implementing Policy Classes + + To define a new policy class, derive from the corresponding base + class for your policy axies. The only policy axis which might + possibly need to be extended are the addressing policy + (AddressingPolicyBase) and the buffering policy + (BufferingPolicyBase). See the Documentation of these classes for + more information on which members can be implemented. + + All members you define must be static. For any of the policy + classes, you must only define those members which are supported by + your implementation. If you leave out a member you automatically + disable the corresponding functionality in the + ClientSocketHandle/ServerSocketHandle interface. + + The member prototypes given in the base class documentation only + specify the call signature not the way, the member must be defined + (FileHandle really is not a FileHandle but an arbitrary + SocketHandle). + + If the existence of a member depends on other policies, you should + use the IfSomePolicyIs and + IfSomePolicyIsNot templates to + dynamically enable/disable the member depending on some other + policy: + + \code + struct ExampleAddressingPolicy + { + template + void connect(senf::SocketHandle handle, Address & addr, + typename senf::IfCommmunicationPolicyIs< + Policy, senf::ConnectedCommunicationPolicy>::type * = 0); + }; + \endcode + + The \c connect member in this example will only be enabled, it + the communication policy of the socket handle is + ConnectedCommunicationPolicy (or a derived type). See Boost.Enable_If + for a discussion of the third argument (\c + senf::ConnectedCommunicationPolicyIs is based on the \c + boost::enable_if template). + + \see \ref policy_framework \n + \ref extend_policy \n + The Boost enable_if utility \n + The Boost.MPL library \n + The Boost.Preprocessor library + + \idea We could combine all the \e Policy \c Is templates into a + single template. Since the \e trait argument will automatically + specify the axis to be used, it is not necessary to specify that + axis in the tempalte functor's name. We could even combine this + with \c SocketPolicyIsBaseOf. + */ + #ifndef HH_SocketPolicy_ #define HH_SocketPolicy_ 1 // Custom includes +#include "GenericSockAddr.hh" + //#include "SocketPolicy.mpp" ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { + /// \addtogroup policy_group + /// @{ // This may be adapted to change the supported policies (however, // ClientSocketHandle and ServerSocketHandle will probably have to - // be adjusted accordingly). However, AddressingPolicy MUST always - // be the first Policy member ... - -# define SENF_SOCKET_POLICIES \ - (AddressingPolicy) \ - (FramingPolicy) \ - (CommunicationPolicy) \ - (ReadPolicy) \ - (WritePolicy) \ + // be adjusted accordingly) + + /** \brief List all policy axis + + \internal + + This define symbol is used to configure the policy axis. The + base class for each of these axis must be defined explicitly + (e.g. AddressingPolicyBase). The implementation files will + then automatically generate all the other classes from this + list. + + \see policy_group + */ +# define SENF_SOCKET_POLICIES \ + (AddressingPolicy) \ + (FramingPolicy) \ + (CommunicationPolicy) \ + (ReadPolicy) \ + (WritePolicy) \ (BufferingPolicy) + + // Wer define these classes explicitly (and not with some macro + // magic) because + // a) AddressingPolicyBase is different from all the others + // b) We want to document each one explicitly + + /** \brief Policy defining socket addressing + + AddressingPolicyBase is the baseclass of all addressing policy + classes. When defining a new addressing policy, the following + members can be defined. All methods must be static. + + + + + + + +
typedef Address Address type
method void local(FileHandle, Address &) Get local socket address
method void peer(FileHandle, Address &) Get remote socket address
method void bind(FileHandle, Address const &) Bind socket to local address
method
void connect(FileHandle, Address const &) Connect to remote address
+ + \see policy_group + */ + struct AddressingPolicyBase + { + virtual ~AddressingPolicyBase() {} + + typedef GenericSockAddr Address; + }; + + /** \brief Policy defining the framing format + + This policy does not define any operations since it does have + no influence on any method signature. It does however affect + the semantics of the \c read() and \c write() operations. + + \note This policy axis probably only has two sensible statess: + StreamFramingPolicy and DatagramFramingPolicy. + + \see policy_group + */ + struct FramingPolicyBase + { + virtual ~FramingPolicyBase() {} + }; + + /** \brief Policy defining, how peers are selected + + The CommunicationPolicy may define two members: + + + + +
method void listen(FileHandle, unsigned backlog) Switch socket into listening state
method int accept(FileHandle, Address &) Accept a new connection
+ + The \c listen member is straight forward. The \c accept() member + must return a new file descriptor (which will be used to + create a new SocketHandle of the correct type). Additionally, + accept() should only be defined, if the Addressing policy is + not \c NoAddressingPolicy (which together with + ConnectedCommunicationPolicy would identify a point-to-point + link with fixed communication partners). + + \note This Policy only has two meaningful states: + ConnectedCommunicationPolicy and + UnconnectedCommunicationPolicy. It is probably not sensible to + define a new CommunicationPolicy type. + + \see policy_group + */ + struct CommunicationPolicyBase + { + virtual ~CommunicationPolicyBase() {} + }; + + /** \brief Policy defining the readability + + The ReadPolicy defines, wether the socket is readable. It + may define two members: + + + + +
method unsigned read(FileHandle, char * buffer, unsigned size) read data from socket
method unsigned readfrom(FileHandle, char * buffer, unsigned size, Address &) read data from unconnected socket
+ + The second member should only be enabled if the communication + policy is UnconnectedCommunication (otherwise it does not make + sense since the communication partner is fixed) (see + AddressingPolicyBase on how to do this). + + \note This Policy only has two meaningful states: + ReadablePolicy and NotReadablePolicy. It probably does not + make sense to define new read policy types. + + \see policy_group + */ + struct ReadPolicyBase + { + virtual ~ReadPolicyBase() {} + }; + + /** \brief Policy defining the writability + + The WritePolicy defines, wether the socket is writable. It may + define two members: + + + + +
method unsigned write(FileHandle, char * buffer, unsigned size) read data from socket
method unsigned writeto(FileHandle, char * buffer, unsigned size, Address &) read data from unconnected socket
+ + The second member should only be enabled if the communication + policy is UnconnectedCommunication (otherwise it does not make + sense since the communication partner is fixed) (see + AddressingPolicyBase on how to do this). + + \note This Policy only has two meaningful states: + WritablePolicy and NotWritablePolicy. It probably does not + make sense to define new write policy types. + + \see policy_group + */ + struct WritePolicyBase + { + virtual ~WritePolicyBase() {} + }; + + /** \brief Policy defining the buffering interface + + The BufferingPolicy defines the buffer handling of the + socket. It may provide the follogin members: + + \see policy_group + */ + struct BufferingPolicyBase + { + virtual ~BufferingPolicyBase() {} + }; // The implementation file will for each Policy declared above // define the following (SomePolicy is one of the above): @@ -59,6 +410,126 @@ namespace senf { // template SocketPolicy< ..policies.. > // template MakeSocketPolicy< ..args.. > // template SocketPolicyIsBaseOf< Base, Derived > + +# ifdef DOXYGEN + + // The following stub definitions are only visible to doxygen + + /** \brief Alias of AddressingPolicyBase for better readability + \see \ref policy_group + */ + typedef AddressingPolicyBase UnspecifiedAddressingPolicy; + + /** \brief Check single policy axis + + This template is an example of the \i Policy \c Is family of + tempalte metafunctions. It will check, wether \c Trait is a + valid compatible Policy class of \c SocketPolicy. \c Trait + must be derived from AddressingPolicyBase (respectively \i + Policy \c Base). + + \see \ref policy_group + */ + template + struct AddressingPolicyIs + {}; + + /** \brief Enable template overload depending on policy value + + This template is an exmaple of the \c If \i Policy \c Is + family of templates. It is used like Boost.enable_if + to enable a templated overload only, if the AddressingPolicy + of \i Policy is compatible with \c Trait (that is the + AddressingPolicy of \c Policy is derived from \c Trait). + + \see policy_group + */ + template + struct IfAddressingPolicyIs + {}; + + /** \brief Inversion of \c IfAddressingPolicyIs + \see policy_group + */ + template + struct IfAddressingPolicyIsNot + {}; + + /** \brief Baseclass of all SocketPolicies + + \internal + + This class is used to + + \see policy_group + */ + struct SocketPolicyBase + {}; + + /** \brief Collection of policy classes + + The SocketPolicy template defines the complete Policy used by + the socket library. It contains one policy class for each + policy axis. + + A SocketPolicy can be complete or incomplete. An incomplete + SocketPolicy will have at least one axis set to \c Undefined + \i Policy (or a generic derived class which is used to group + some other policies but does not (completely) define the + policy behavior). A complete SocketPolicy will have a + concrete definition of the desired behavior for each policy + axis. + + \see policy_group + */ + template < + class AddressingPolicy, + class FramingPolicy, + class CommunicationPolicy, + class ReadPolicy, + class WritePolicy, + class BufferingPolicy > + struct SocketPolicy + {}; + + /** \brief Metafunction to create SocketPolicy + + This template metafunction simplifies the creation of a + SocketPolicy instantiation. It takes any number (that is up to + 6) of Policy classes as arguments in any Order. It will create + a SocketPolicy from these policy classes. Any axis not + specified will be left as \c Unspecified \i Policy. + + \see policy_group + */ + template + struct MakeSocketPolicy + {}; + + /** \brief Check policy compatibility + + This tempalte metafunction checks, wether the SocketPolicy \c + Derived is more specialized than \c Base (and therefore a + SocketHandle with policy \c Derived is convertible to a + SocketHandle with policy \c Base). + + The metafunction will return true (that is inherits from \c + boost::true_type, see the Boost.MPL + library documentation for more information) if each policy + class in \c Base is a baseclass of (or the same as) the + corresponding policy class in \c Derived. + + \see policy_group + */ + template + struct SocketPolicyIsBaseOf + {}; + +# endif + + /// @} } //////////////////////////////hh.e//////////////////////////////////////// diff --git a/Socket/SocketPolicy.ih b/Socket/SocketPolicy.ih index dabdf26..fac273a 100644 --- a/Socket/SocketPolicy.ih +++ b/Socket/SocketPolicy.ih @@ -46,49 +46,32 @@ #include #include // for enable_if -#include "GenericSockAddr.hh" - ///////////////////////////////ih.p//////////////////////////////////////// -namespace senf { +/// \cond disabled +// Hide this code from doxygen +namespace senf { # define SENF_SOCKET_POLICIES_N BOOST_PP_SEQ_SIZE( SENF_SOCKET_POLICIES ) - // This REALLY is bad ... but we just need an Address member in - // AddressingPolicyBase as long as ClientSocketHandle / - // ServerSocketHandle don't make use of enable_if for each and - // every member they define ... - - struct AddressingPolicyBase - { - virtual ~ AddressingPolicyBase() {} - - typedef GenericSockAddr Address; - }; - -# define SP_DeclareBase(x1,x2,SomePolicy) \ - struct BOOST_PP_CAT(SomePolicy,Base) \ - { virtual ~ BOOST_PP_CAT(SomePolicy,Base) () {} }; \ +# define SP_DeclareAlias(x1,x2,SomePolicy) \ typedef BOOST_PP_CAT(SomePolicy,Base) BOOST_PP_CAT(Unspecified,SomePolicy); - BOOST_PP_SEQ_FOR_EACH( SP_DeclareBase, , BOOST_PP_SEQ_POP_FRONT( SENF_SOCKET_POLICIES ) ) + BOOST_PP_SEQ_FOR_EACH( SP_DeclareAlias, , SENF_SOCKET_POLICIES ) -# undef SP_DeclareBase +# undef SP_DeclareAlias struct SocketPolicyBase { virtual ~SocketPolicyBase() {} -# define SP_DeclareTypedef(x1,x2,SomePolicy) \ - typedef BOOST_PP_CAT(SomePolicy,Base) SomePolicy; \ - BOOST_PP_CAT(SomePolicy,Base) BOOST_PP_CAT(BOOST_PP_CAT(the,SomePolicy),_); \ - virtual BOOST_PP_CAT(SomePolicy,Base) const & BOOST_PP_CAT(the,SomePolicy) () const \ - { return BOOST_PP_CAT(BOOST_PP_CAT(the,SomePolicy),_); } +# define SP_Declare(x1,x2,SomePolicy) \ + virtual BOOST_PP_CAT(SomePolicy,Base) const & BOOST_PP_CAT(the,SomePolicy) () const = 0; - BOOST_PP_SEQ_FOR_EACH( SP_DeclareTypedef, , SENF_SOCKET_POLICIES ) + BOOST_PP_SEQ_FOR_EACH( SP_Declare, , SENF_SOCKET_POLICIES ) -# undef SP_DeclareTypedef +# undef SP_Declare }; # define SP_TemplateArgs(x1,x2,n,SomePolicy) \ @@ -130,15 +113,15 @@ namespace impl { BOOST_PP_IIF( BOOST_PP_EQUAL(n,m), Policy, typename Base::SomePolicy ) # define BOOST_PP_LOCAL_LIMITS (0, BOOST_PP_DEC( SENF_SOCKET_POLICIES_N ) ) -# define BOOST_PP_LOCAL_MACRO(n) \ - SocketPolicy_rv MakeSocketPolicy_merge_(BOOST_PP_CAT( BOOST_PP_SEQ_ELEM( n, SENF_SOCKET_POLICIES ),Base)*); \ - \ - template \ - struct MakeSocketPolicy_merge)> \ - { \ - typedef SocketPolicy< \ - BOOST_PP_SEQ_FOR_EACH_I( SP_DeclareMakeSocketPolicy_merge_member, n, SENF_SOCKET_POLICIES ) \ - > type; \ +# define BOOST_PP_LOCAL_MACRO(n) \ + SocketPolicy_rv MakeSocketPolicy_merge_(BOOST_PP_CAT( BOOST_PP_SEQ_ELEM( n, SENF_SOCKET_POLICIES ),Base)*); \ + \ + template \ + struct MakeSocketPolicy_merge)> \ + { \ + typedef SocketPolicy< \ + BOOST_PP_SEQ_FOR_EACH_I( SP_DeclareMakeSocketPolicy_merge_member, n, SENF_SOCKET_POLICIES ) \ + > type; \ }; # include BOOST_PP_LOCAL_ITERATE() @@ -178,8 +161,7 @@ namespace impl { # undef SP_DeclareArguments template - SocketPolicy_rv<2> SocketPolicy_checkcompat_( - BOOST_PP_ENUM_PARAMS( SENF_SOCKET_POLICIES_N, void * BOOST_PP_INTERCEPT ) ); + SocketPolicy_rv<2> SocketPolicy_checkcompat_( ... ); template struct SocketPolicy_checkcompat @@ -243,6 +225,8 @@ namespace impl { } +/// \endcond + ///////////////////////////////ih.e//////////////////////////////////////// #endif diff --git a/Socket/SocketProtocol.hh b/Socket/SocketProtocol.hh index fe42ee8..0335412 100644 --- a/Socket/SocketProtocol.hh +++ b/Socket/SocketProtocol.hh @@ -20,6 +20,36 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + */ + +/** \defgroup protocol_group The Protocol Classes + + \image html Protocols.png + + The socket handle classes and templates only implement the most + important socket API methods using the policy framework. To access + the complete API, the protocol interface is provided. Access to + the protocol interface is only possible via + senf::ProtocolClientSocketHandle and + senf::ProtocolServerSocketHandle which have the necessary \c + protocol() member. This member returns a reference to the protocol + class instance which contains members covering all the API + functions (mostly setsockopt/getsockopt related calls but there + may be more, this is completely up to the implementor of the + protocol class) not found in the SocketHandle interface. The + protocol interface is specific to the protocol. It's + implementation is quite free. The standard protocols are + implemented using a simple multiple-inheritance hierarchy as shown + above. + + Since the protocol class is protocol specific (how intelligent + ...), the protocol class also defines the complete socket policy + to be used with it's protocol. Complete meaning, that every policy + axis must be assigned it's the most specific (that is derived) + policy class to be used with the protocol. + */ + #ifndef HH_SocketProtocol_ #define HH_SocketProtocol_ 1 @@ -33,6 +63,8 @@ namespace senf { + /// \addtogroup protocol_group + /// @{ class SocketPolicyBase; @@ -107,6 +139,7 @@ namespace senf { }; + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/doclib/Doxyfile.global b/doclib/Doxyfile.global index ac07ee1..d2b544d 100644 --- a/doclib/Doxyfile.global +++ b/doclib/Doxyfile.global @@ -1,11 +1,12 @@ OUTPUT_DIRECTORY = doc INPUT = . FILE_PATTERNS = *.c *.cc *.cci *.ct *.cti *.h *.hh *.ih *.mmc *.dox -EXCLUDE_PATTERNS = *.test.cc .* +EXCLUDE_PATTERNS = *.test.cc *.test.hh .* IMAGE_PATH = . ALIASES = "fixme=\xrefitem fixme \"Fix\" \"Fixmes\"" \ - "idea=\xrefitem idea \"Idea\" \"Ideas\"" + "idea=\xrefitem idea \"Idea\" \"Ideas\"" \ + "implementation=\par \"Implementation note:\"" REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = YES MULTILINE_CPP_IS_BRIEF = YES @@ -16,7 +17,6 @@ EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES INTERNAL_DOCS = YES SOURCE_BROWSER = YES -STRIP_CODE_COMMENTS = NO ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 3 diff --git a/run-test-gdb.sh b/run-test-gdb.sh new file mode 100755 index 0000000..34dc2ba --- /dev/null +++ b/run-test-gdb.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +# This script helps finding the origin of unhandled exceptions in the +# unit tests. The unit test framework will tell, that an exception has +# been caught by the test driver but will not tell, where in the code +# it has occured. +# +# This script will run the .test.bin test driver within gdb and will +# create a backtrace for every exception caught by the test driver. +# +# NOTE: If your unit test (excplicitly) writes output to stderr, this +# output will be lost +# +# NOTE: This works by setting a breakpoint in the std::exception +# constructor. This is, where the backtrace is created from. If you do +# some funky manipulations with your exceptions, the backtrace might +# not point to the throw statement, however this is very unusual. Of +# course, this only works, if all your exceptions are derived from +# std::exception but that should always be the case. + +trap 'rm -f .run-test-gdb.cmd' 0 1 2 15 + +# This gdb script will set the breakpoint and will then run the test +# driver. Whenever the execution stops, it will print a backtrace and +# will then continue execution. This will produce superflous +# backtraces for exceptions which are handled correctly. These will be +# filtered out later. +cat >.run-test-gdb.cmd </dev/null | perl -e ' + while () { + if (/^$/) { + $_=; + if (/^Breakpoint 1, exception/) { + @l=(); + while () { + last unless /^#?[0-9]|^ /; + push @l,$_ if /^#/; + $l[$#l] .= $_ if /^ /; + } + if (/: fatal error in /) { + for (@l[1..$#l]) { + last if /^#[0-9]+ +0x[0-9a-f]+ in boost::unit_test/; + print; + } + } + } + else { print "\n"; } + } + print; + } +' \ No newline at end of file