![]() | |
![]() ![]() |
![]() | Imperfect C++ Practical Solutions for Real-Life Programming By Matthew Wilson |
Table of Contents | |
Chapter 3. Resource Encapsulation |
3.3. Wrapper ProxiesA small, but often useful, step up from POD types is wrapper proxies. Such types do nothing to address the automatic acquisition or release of resources; they merely make it easier to use the types that they wrap. Consider that you're working with a source-parser plug-in product, whereby you register a dynamic library (see Chapter 9) that receives notifications from a parsing engine in response to certain events. The entry point might look like Listing 3.1:[1]
Listing 3.1.enum SPEvent { . . . }; struct ParseContext { // Callback functions void *(*pfnAlloc)(size_t cb); void (*pfnFree)(void *p); void *(*pfnRealloc)(void *p, size_t cb); int (*pfnLookupSymbol)( char const *name, char *dest , size_t *pcchDest); // Data members . . . char const *currTokBegin; char const *currTokEnd; . . . }; int SrcParse_LibEntry(SPEvent event, ParseContext *context); Clearly, dealing with the ParseContext structure is going to be messy, as in Listing 3.2. Listing 3.2.// MyPlugIn.cpp int SrcParse_LibEntry(SPEvent event, ParseContext *context) { switch(event) { case SPE_PARSE_SYMBOL: MyPlugIn_ParseSymbol(context); . . . } void MyPlugIn_ParseSymbol(ParseContext *context) { size_t tokLength; char *tokName; size_t cchDest; tokLength = 1 + (context->currTokEnd –context->currTokBegin); tokName = strncpy( (char*)(*context->pfnAlloc)(sizeof(char) * tokLength), context->currTokBegin, tokLength); size_t cchDest = 0; int cch = (*context->pfnLookupSymbol)(tokenName, NULL, &cchDest); . . . } Not pretty is it? As well as being a royal pain to deal with all that code, we're messing around with raw pointers in our logic code, and the handling of errors and exceptions is sketchy at best. All these things are what C++ positively excels at, so surely we can improve it. The answer is that most of the complexity can be easily encapsulated within a wrapper proxy Listing 3.3.class ParseContextWrapper { public: ParseContextWrapper(ParseContext *context) : m_context(context) {} void *Alloc(size_t cb) { return (*m_context->pfnAlloc)(cb); } string CurrentToken() { return string(m_context->currTokBegin, m_context->currTokEnd); } string LookupSymbol(string const &token) { . . . int OnParseSymbol(); private: ParseContext *m_context; }; which leads to considerably simpler user code. Listing 3.4.int SrcParse_LibEntry(SPEvent event, ParseContext *context) { try { ParseContextWrapper cw(context); switch(event) { case SPE_PARSE_SYMBOL: return OnParseSymbol(); . . . } catch(ParseException &x) { . . . APIs such as this represent the stereotypical wrapper proxy case. ParseContextWrapper does not in any way own the ParseContext pointer it is given, it merely enables client code to work with a simpler, and presumably well-tested, interface over the base API. Note that I've made the m_context member private out of habit; you could arguably leave it public, since the client code has access to the raw ParseContext anyway, but old habits die hard.[2]
|
![]() | |
![]() ![]() |