The Policy Framework


Detailed Description

WritePolicyBase AddressingPolicyBase FramingPolicyBase SocketPolicy CommunicationPolicyBase ReadPolicyBase SocketPolicy

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

The template senf::SocketPolicy combines these policy axis to form a concrete socket policy. In a concrete policy, each of these policy axis is assigned a value, the policy value. This value is identified by a class type, a policy class. E.g. possible values for framingPolicy are DatagramFramingPolicy or StreamFramingPolicy which are classes derived from the axis base class FramingPolicyBase. This base class also doubles as UnspecifiedFramingPolicy (which is just a typedef alias). If a policy axis is assigned this Unspecified type, the axis is left unspecified, the concrete policy will be incomplete.

The senf::SocketPolicy template defines the behavior of a socket handle. The socket handle instances do not implement any socket functionality themselves instead deferring the implementation to the policy classes. The SocketHandle interface is therefore not implemented using virtual members, all important socket functions can be inlined by the compiler to create highly efficient code.

A senf::SocketPolicy instance can be incomplete. In this case it does not completely specify the socket interface, it leaves some aspects open by assigning the Unspecified value to one or more of the policy axis. A senf::SocketHandle based on such a policy will have a reduced interface: It will only support those members for which the corresponding policies are defined.

To build a senf::SocketPolicy instance the senf::MakeSocketPolicy helper is provided. This helper template takes any number (it is really limited to 6 Arguments but more arguments don't make sense) of policy classes as it's argument. The MakeSocketPolicy helper will take the arguments in the order they are specified and for each argument will check to which axis the policy class belongs (by checking the base classes of that class) and assign it to the correct policy axis in the senf::SocketPolicy template. If any policy axis are not specified, they are defaulted to their corresponding Unspecified value. This helper frees you to specify the policy classes in any order. An additional feature is, that you may specify a complete policy as a first argument. This policy will then be used to provide default values for unspecified axis.

Two senf::SocketHandle's with different policies can be compatible. If they are, the more specific SocketHandle can be converted (assigned to) the more basic SocketHandle. A SocketHandle is more specific then another SocketHandle if the policy of the former is more specific then that of the latter which means, that for each policy axis separately, the value of that axis of the more specific policy is derived from or the same as the value of that axis in the more basic policy. This is like converting a derived class pointer to a base class pointer, only it happens separately but at the same time for each policy axis:

// This defines an incomplete policy where addressingPolicy and writePolicy
// are unspecified
typedef senf::MakeSocketPolicy<
    senf::StreamFramingPolicy,
    senf::ConnectedCommunicationPolicy,
    senf::ReadablePolicy
    >::policy MyReadableSocketPolicy

typedef senf::ClientSocketHandle<MyReadableSocketPolicy> MyReadableHandle;

// TCPv4ClientSocketHandle is a socket handle with the policy equivalent to
// senf::MakeSocketPolicy<
//     INet4AddressingPolicy,
//     StreamFramingPolicy,
//     ConnectedCommunicationPolicy,
//     ReadablePolicy,
//     WritablePolicy>::policy
senf::TCPv4ClientSocketHandle tcpHandle (...);

MyReadableHandle myHandle (tcpHandle); // Conversion to more basic socket handle

The Policy Framework Classes

In the following discussion, deeper insight into C++ and especially the concepts of template meta-programming are needed. However, this information is only needed if you want to write new policy classes or want to use the policy framework explicitly for your own involved optimizations ... or if you are just plain curious :-)

In the following discussion we will use the following conventions:

  • Axis is one or AddressingPolicy, FramingPolicy, CommunicationPolicy, ReadPolicy or WritePolicy
  • socketPolicy is any socket policy (that is, an instantiation of the SocketPolicy template)
  • 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):
Axis Base (ex: AddressingPolicyBase)
Baseclass of all policies in this axis
Unspecified Axis (ex: UnspecifiedAddressingPolicy)
An alias (typedef) for Axis Base
Axis Is < socketPolicy, trait > (ex: AddressingPolicyIs)
A template metafunction returning boost::true_type, if trait (any class derived from Axis Base) is a compatible policy value of the given socketPolicy
If Axis Is < socketPolicy, trait > (ex: IfAddressingPolicyIs)
This is a combination of Axis Is and boost::enable_if
If Axis IsNot < socketPolicy, 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.

class 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 checking the template directly).
template SocketPolicy < addressingPolicy, framingPolicy, communicationPolicy, readPolicy, writePolicy >
This is the central SocketPolicy template. It combines a complete set of policy classes, one for each axis.
template MakeSocketPolicy < args >
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 Unspecified Axis. Additionally, the first Argument may optionally be an arbitrary SocketPolicy. It will provide default values for unspecified axis
template SocketPolicyIsBaseOf < base, derived >
This template metafunction will check, whether the socket policy derived is convertible to base. This means, that for each axis, the corresponding policy class in derived must be derived or be the same as the one on base.
Implementation note:
All these classes are created automatically. The SENF_SOCKET_POLICIES macro is a Boost.Preprocessor style sequence listing all policy axis. The Boost.Preprocessor library is then used to generate the respective classes.

Implementing Policy Classes

To define a new policy class, derive from the corresponding base class for your policy axes. The only policy axis which might possibly need to be extended is the addressing policy (AddressingPolicyBase). 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:

  struct ExampleAddressingPolicy
  {
      template <class SPolicy>
      void connect(senf::SocketHandle<SPolicy> handle, Address & addr,
                   typename senf::IfCommmunicationPolicyIs<
                       SPolicy, senf::ConnectedCommunicationPolicy>::type * = 0);
  };

The 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 (senf::ConnectedCommunicationPolicyIs is based on the boost::enable_if template).

See also:
Extending the policy framework
The Boost enable_if utility
The Boost.MPL library
The Boost.Preprocessor library
Idea:
We could combine all the Axis Is templates into a single template. Since the trait argument will automatically specify the axis to be used, it is not necessary to specify that axis in the template functor's name. We could even combine this with SocketPolicyIsBaseOf.

Classes

struct   senf::AddressingPolicyBase
  Policy defining socket addressing. More...
struct   senf::FramingPolicyBase
  Policy defining the framing format. More...
struct   senf::CommunicationPolicyBase
  Policy defining, how peers are selected. More...
struct   senf::ReadPolicyBase
  Policy defining the readability. More...
struct   senf::WritePolicyBase
  Policy defining the writability. More...
struct   senf::AddressingPolicyIs< SocketPolicy, Trait >
  Check single policy axis. More...
struct   senf::IfAddressingPolicyIs< SocketPolicy, Trait >
  Enable template overload depending on policy value. More...
struct   senf::IfAddressingPolicyIsNot< SocketPolicy, Trait >
  Inversion of IfAddressingPolicyIs. More...
struct   senf::SocketPolicyBase
  Baseclass of all SocketPolicies. More...
struct   senf::SocketPolicy< AddressingPolicy >
  Collection of policy classes. More...
struct   senf::MakeSocketPolicy< Arg1, Arg2, ArgN >
  Metafunction to create SocketPolicy. More...
struct   senf::SocketPolicyIsBaseOf< Base, Derived >
  Check policy compatibility. More...

Modules

  Policy Implementation classes

Defines

#define  SENF_SOCKET_POLICIES
  List all policy axis.

Typedefs

typedef AddressingPolicyBase  senf::UnspecifiedAddressingPolicy
  Alias of AddressingPolicyBase for better readability.

Define Documentation

#define
SENF_SOCKET_POLICIES

Value:

(AddressingPolicy)                      \
        (FramingPolicy)                         \
        (CommunicationPolicy)                   \
        (ReadPolicy)                            \
        (WritePolicy)
List all policy axis.

For internal use only.

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 also:
The Policy Framework

Definition at line 266 of file SocketPolicy.hh.


Typedef Documentation

typedef AddressingPolicyBase senf::
UnspecifiedAddressingPolicy

Alias of AddressingPolicyBase for better readability.

See also:
The Policy Framework

Definition at line 409 of file SocketPolicy.hh.