Lecture Notes for Advanced Programming II

3 April 2001 - Wrappers


  1. what is wrapping

    1. turning a library or set of routines into another library or set of routines

    2. object-oriented languages are good for wrapping because objects are natural wrappers having features different from libraries and routine sets

  2. why wrap

    1. make a given library or routine set more usable - sockets

    2. hide the differences between different implementations of the same features - unix vs microsoft posix implementations; unix vs unix implementations

    3. merge similar but different libraries into a generic library - graphical or thread libraries

  3. for example, dbm the unix database library

    1. dbm - manages (key, data) pairs

      1. dbminit(f) - open the database named f

      2. dbmclose() - close the opened database

      3. fetch(k) - fetch the data associated with key k

      4. store(k,d) - associate data d with key k

      5. delete(k) - delete key k and associated data

      6. firstkey() - return the first key

      7. nextkey(k) - return the key after key k

    2. there are at least three versions, all different

      1. dbm - the original and obsolete

      2. ndbm - dbm's replacement; named files

      3. gdbm - gnu's ndbm; more functions than ndbm, different error handling

  4. wrapper interface design

    1. what's at the interface

      1. intersection approach recommended, but produces the lcd

      2. one-file dbm access

      3. modified intersection - makes dbm expensive, but dbm is obsolete

    2. what's the interface look like

      1. consistency with existing libraries - but the libraries are inconsistent

      2. the intersection approach again - this time gdbm loses

      3. missing functions can either be dealt with (exists), ignored (error function), or handled behind the scenes (reorganize)

      4. if the function's useful or expensive, make it visible

      5. solution - make the interface look like ndbm with cleaned up error handling; extra gdbm functions are ignored or used behind the scenes

  5. wrapper implementation

    1. keep an eye out for shared, global data - the error value

    2. the error return problem - only one return value

    3. constructor failures

      1. the constructor can't return values - indicating failure is a problem

      2. can't ignore it - leads to undefined behavior; robust failure

      3. a separate error return member function

        1. class simple_dbm {
            public:
              simple_dbm() : error(0) { 
                if ((error = f())) return ;
                }
              int get_error(void) const { return error; }
            private:
              int error;
            }
          

        2. a common solution, familiar but perhaps too complicated

      4. return an error code via a constructor argument

        1. class simple_dbm {
            public:
              simple_dbm(int * error = NULL) {
                if ((e = f()) {
                  if (error != NULL) *error = e;
          	return;
          	}
                }
            }
          

        2. now the user can either get errors or not

        3. an unfamiliar idiom, but not an obscure one

        4. but every function should treat errors consistently

        5. also, user has to take the address of the error argument

        6. reference parameters can take care of the last problem

        7. class simple_dbm {
            public:
              simple_dbm(int error & = default_error) {
                if ((error = f()) return;
          	}
                }
            private:
              static int default_error;
            }
          int simple_dbm::default_error;
          

        8. the external declaration is clumsy, but this is a nice solution

        9. the initializer-list problem

      5. you can also use exceptions

        1. exceptions are almost always a bad idea

        2. exceptions are like super-gotos - they can branch outside the enclosing procedure

        3. the try-catch statement is ugly, cluttering up the code much more than does error-return checking code

  6. virtual functions

    1. you can wrap each library directly in a class, or use virtual classes

    2. virtual functions make clear the porter's responsibility

    3. virtual functions make it easy to configure among possibilities

      1. code includes simple-db.h

      2. program links with gdbm-simple-db.o or ndb-simple-db.o


This page last modified on 6 April 2001.