A.2 Use Wrapper Facades to Enhance Type Safety

I l @ ve RuBoard

Higher-level programming languages, such as C++ and Java, provide enhanced type safety, which ensures that operations can't be applied to objects that don't support them. Ideally , type errors should be caught at compile time. However, OS libraries, such as those that implement the C run time, Sockets, or Pthreads, seldom expose internal data structures since that would

  • Compromise efforts to provide upward compatibility

  • Make multiple programming language support hard on some platforms

  • Allow access to private details that should not be used by applications

OS APIs therefore often expose a C-like interface, using low-level, opaque representations of their internal data structures, for example, I/O handles and socket handles. These opaque types make it hard for compilers to detect type errors. Consequently, these errors can be detected only at run time, which increases development and debugging effort, complicates error handling, and reduces application robustness. This section describes a design principle that enhances type safety at compile time, as well as a principle that provides an escape hatch for cases in which strict type safety is overly restrictive .

A.2.1 Design C++ Classes That Force Correct Usage

Problem: Many limitations with the Socket API discussed in Section 2.3 stem from the following problems caused by its lack of type safety:

  1. Nonportable ” The type names and underlying representations of I/O handles vary across platforms, making them nonportable. For example, socket handles are integers on UNIX platforms and pointers on Win32.

  2. Easily misused ” Low-level representations permit I/O handles to be misused in ways that can be detected only at run time. For example, the Socket API can easily be applied incorrectly by invoking the accept() function on a data-mode socket handle that's intended to transfer data via recv() and send() .

Solution Design C++ classes that force correct usage. Weakly typed I/O handles can be encapsulated in C++ classes that export strongly typed methods for that class's users. When application code uses these new classes, C++ compilers can ensure that only legal methods are invoked.

The following C++ code illustrates the application of this principle to the echo_server () function on page 37 in Section 2.3.1:

 int echo_server (const ACE_INET_Addr &addr) {   ACE_SOCK_Acceptor acceptor;  // Connection factory.   ACE_SOCK_Stream peer_stream; // Data transfer object.   ACE_INET_Addr peer_addr;     // Peer address object.   // Initialize the passive acceptor and accept a new connection.   if (acceptor.open (addr) != -1       && acceptor.accept (peer_stream, &peer_addr) != -1) {     char buf[BUFSIZ];     for (size_t n; (n =peer_stream.recv (buf, sizeof buf)) > 0;)       // send_n() handles "short writes."       if (peer_stream.send_n (buf, n) != n)         // Error handling omitted...   } } 

This revision solves many problems with the Socket API and the use of C. For example, the ACE_SOCK_Acceptor class only exposes methods suitable for passively establishing connections. Since these wrapper facade classes are strongly typed, invalid operations are detected at compile time rather than at run time. It's therefore not possible to invoke recv()/send() on an ACE_SOCK_Acceptor or accept() on an ACE_SOCK_Stream since these methods aren't part of those wrapper facade interfaces.

A.2.2 Allow Controlled Violations of Type Safety

Problem: As shown above, wrapper facades can shield networked applications from error-prone or platform-specific implementation details, such as whether a socket handle is represented as an integer or a pointer. Situations arise, however, in which this additional abstraction and type safety actually prevent developers from using a wrapper facade in useful ways not anticipated by its original designer. This frustrating experience can discourage developers from leveraging the other important benefits of wrapper facades.

Solution Allow controlled violations via "escape hatches." The intent of this principle is to make it easy to use wrapper facades correctly, hard to use them incorrectly, but not impossible to use them in ways that class designers did not anticipate originally. This principle is exemplified by the get_handle() and set_handle() methods in ACE_IPC_SAP , which is the root base class of the ACE IPC wrapper facades described in Section 3.3 on page 52. Exposing these two methods to get or set an I/O handle allows applications to circumvent ACE IPC wrapper facade type checking when applications must use handle-based system functions.

For example, it may be necessary to obtain a socket handle for use with the ACE_Handle_Set class and select() function, as shown below:

 ACE_SOCK_Acceptor acceptor; ACE_Handle_Set ready_handles; // . . . if (ready_handles.is_set (acceptor.get_handle ()) ACE::select ((int)acceptor.get_handle () + 1, ready_handles ()); 

Naturally, these escape hatch mechanisms should be used sparingly since they decrease portability and increase the potential for errors, thereby defeating key benefits of the Wrapper Facade pattern. Situations where it's necessary to mix object-oriented and non-object-oriented abstractions, such as within the implementation of certain layers of ACE itself, should ideally be within class implementations and seldom exposed to class users. If the need for escape hatches arises often for class users, it's best to re-examine the class interface design for opportunities to refactor or redesign in order to restore type safety.

I l @ ve RuBoard


C++ Network Programming
C++ Network Programming, Volume I: Mastering Complexity with ACE and Patterns
ISBN: 0201604647
EAN: 2147483647
Year: 2001
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net