Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 5.  Object Access Models


5.1. Vouched Lifetimes

This is the simplest and most efficient manner in which one object can allow another access to the objects that it contains. Put simply, it is part of the documented semantics of the type that it guarantees that the objects that it contains will not live outside its lifetime. This is obvious in the case of composition, as shown in Listing 5.1.

Listing 5.1.


class EnvironmentVariable


{


// Accessors


public:


  String const &GetName()const


  {


    return m_name;


  }


  String const *GetValuePart(size_t index) const


  {


    return index < m_valueParts.size() ? &m_valueParts[index] : NULL;


  }


// Members


private:


  String          m_name;


  Vector<String>  m_valueParts;


};



This is the most efficient access model, because the caller is given a direct reference (or pointer) to the instance to which it wants access. In the above example, the member variable m_name and a string from the m_valueParts collection are both accessed in the same sense, via vouched lifetime.

However, this model is vulnerable to undefined behavior in situations where the client code maintains and attempts to use a pointer (or reference) that it acquired after the container has ceased to exist. But as long as the client code is written correctly in respect of this constraint, it is a perfectly respectable access model and is one of the most commonly used.

5.1.1 Checkout Model

A twist on the vouched lifetime model, which has a bit more overhead, but helps to ensure that callers behave themselves, is the checkout model. This can aid in the implementation of the container, since it can use the checkout count as a reference count, or as a supplement to any reference counting it may already employ. It can allow exclusive access to the contained instance (either by rejecting subsequent calls, or by blocking), or it can allow the instances to be shared, as appropriate to the situation.

The checkout model works by returning a token to the client code if the requested resource can be accessed and which must be used to "check-in" the resource once it is no longer needed. This is a bit like valet parking: the resource you use is the parking space, and the token is the little slip of paper you receive as a promise that no one's going to go joyriding in your car while you're having your meal. Naturally, something that has been checked out has to be checked in or all will not be right with the world, and of course these checkouts can usually be protected via scoping classes (see Chapter 6), so that the ticket's destructor will ensure the resource is checked in without requiring an explicit call.

Sometimes the accessed value can do double-duty and serve as the token, which can make for a simpler situation all round. The Synesis BufferStore API provides efficient fixed-size fixed-number block containers—useful in multithreaded high-speed network communications systems that efficiently acquire, and optionally share, memory blocks from a preallocated pool. It has a C++ wrapper that looks something like that shown in Listing 5.2.

Listing 5.2.



class BufferStore


{


// Construction


public:


  BufferStore(Size siBuff, UInt32 initial, UInt32 maximum);


// Operations


public:


  // Acquire one or more buffers


  UInt32 Allocate(PVoid buffers[], UInt32 cBuffers);


  // Share many buffers. Each buffer MUST be already acquired


  UInt32 Share(PCVoid srcBuffers[], PVoid destBuffers[]


              , UInt32 cBuffers);


  // Release one or many buffers


  void Release(PVoid buffers[], UInt32 cBuffers);


  . . .



In this case, the buffer pointer values are void*, and also serve as the tokens to return to the store for release, from which they may be subsequently acquired by other client code. Whether you have to check out instances or not, use of the vouched lifetime access model requires the usual mature approach: read the documentation, code in accordance to the documentation, test, level a custard pie at any developer who changes the semantics and not the documentation.


      Previous section   Next section