Page #33 (Optimizations)


Error Reporting

If you reexamine our VCR code, you will notice that although we handle failures gracefully, we haven t done a good job of unequivocally identifying the error conditions. For example, function CreateVCR returns NULL for two cases when there is not enough memory to create the VCR instance and when the requested pszType interface is not found. How is the TV code supposed to know what the real problem is?

It is not really a coding problem. It is the way we designed the interface; it doesn t have any provision for returning error codes.

Let s do something about it.

Every interface method has a possibility of failing. A uniform mechanism for obtaining error status across all methods will simplify client-side code. However, an interface method can fail for a number of reasons, such as:

  • The client ran out of memory while doing some memory allocations.

  • The argument passed to the method had a value that is not acceptable by the server.

  • In case of method Probe, the requested interface was not found.

  • There could be some internal error in the server code that stops it from further processing.

Let s mandate that every interface method return an integer indicating the status. To distinguish this special integer value from other integers, let s define its type as VRESULT (for Video result).

 typedef int VRESULT; 

Let s define some possible error codes:

 #define V_OUTOFMEMORY       1  #define V_NOINTERFACE       2  #define V_INTERNALERROR     3 

If a method call doesn t fail, it should return an OK status:

 #define V_OK 0 

In order to check for success or failures, let s define a couple of macros:

 #define V_SUCCEEDED(P1)     ((P1) == V_OK)  #define V_FAILED(P1)        ((P1) != V_OK) 

Now, let s rework our interface definitions.

Most current method definitions already return a value. This is the logical value that the client needs for its processing. If we change the method definitions to return the error status, we will need to obtain the logical return value as a parameter. For example, the old method prototype

 virtual long _stdcall GetSignalValue() = 0; 

would be modified to

 virtual VRESULT _stdcall GetSignalValue(long* pRetVal) = 0; 

The client usage will need to change accordingly:

 // Old code  long val = pVideo->GetSignalValue();  // New Code  long val;  VRESULT vr = pVideo->GetSignalValue(&val);  if (V_FAILED(vr)) {   ReportError(vr);  } 

With the method definitions changed, the following is our new interface definition header file [1] :

[1] In case you are wondering why I chose C style pointer as a parameter type instead of C++ style reference, semantically, there is no difference between the two styles. I just picked one that COM interface definitions use, as we will see in the next chapter.

 class IGeneral  { public:    virtual VRESULT _stdcall Probe(char* pszType,      IGeneral** ppRetVal) = 0;    virtual void _stdcall AddReference() = 0;    virtual void _stdcall Delete() = 0;  };  class IVideo : public IGeneral  { public:    virtual VRESULT _stdcall GetSignalValue(long* pRetVal) = 0;  };  class ISVideo : public IGeneral  { public:    virtual VRESULT _stdcall      GetSVideoSignalValue(long* pRetVal) = 0;  };  extern "C" VRESULT _stdcall CreateVCR(char* pszType,    IGeneral** ppRetVal); 


Methods AddReference and Delete do not return VRESULT. These two methods deser ve special treatment. They do not do any complex processing. They do not even take any parameters that possibly could be invalid. These two methods should never fail. If they do, you really have a bigger problem somewhere else in your logic.

The VCR code needs to change according to the new method definition.

The following code snippet shows the revised implementation:

 VRESULT CVcr::Probe(char* pszType, IGeneral** ppRetVal)  {   *ppRetVal = NULL;    if (!stricmp(pszType, "general")) {     *ppRetVal = static_cast<IVideo*>(this);    }else    if (!stricmp(pszType, "video")) {     *ppRetVal = static_cast<IVideo*>(this);    }else    if (!stricmp(pszType, "svideo")) {     *ppRetVal = static_cast<ISVideo*>(this);    }    if (NULL != (*ppRetVal)) {     AddReference();      return V_OK;    }    return V_NOINTERFACE;  }  VRESULT _stdcall CreateVCR(char* pszType, IGeneral** ppRetVal)  {   *ppRetVal = NULL;    CVcr* pVcr = new CVcr;    if (NULL == pVcr) {     return V_OUTOFMEMORY;    }    VRESULT vr = pVcr->Probe(pszType, ppRetVal);    if (V_FAILED(vr)) {     delete pVcr;    }    return vr;  } 

Our TV code can now do a better job of error reporting:

 void ReportError(VRESULT vr)  {   char* pszError = NULL;    switch(vr) {   case V_OUTOFMEMORY:      pszError = "Out of memory";      break;    case V_NOINTERFACE:      pszError = "No such interface supported";      break;    case V_INTERNALERROR:      pszError = "Internal error. Contact the VCR vendor";      break;    default:      pszError = "Unknown error";    }    cout << pszError << endl;  }  int main(int argc, char* argv[])  {   IGeneral* pGeneral = NULL;    VRESULT vr = CreateInstance("vcr.dll", "svideo", &pGeneral);    if (V_FAILED(vr)) {     ReportError(vr);      return 1;    }    UseSVideo(reinterpret_cast<ISVideo*>(pGeneral));    pGeneral->Delete();    return 0;  }  void UseSVideo(ISVideo* pSVideo)  {   long val;    VRESULT vr;    for(int i=0; i<10; i++) {     vr = pSVideo->GetSVideoSignalValue(&val);      if (V_FAILED(vr)) {       ReportError(vr);        continue;      }      cout << "Round: " << i << " - Value: " << val << endl;    }  } 

A small coding style change is in order. In the main function above, a call to CreateInstance returns a value of type IGeneral*, even though we are requesting an svideo interface pointer. As we know that the pointer returned (pGeneral) is really a pointer to ISVideo, we just reinterpret pGeneral to ISVideo* later in the code. Instead, we might as well specify a return value argument of type ISVideo* as shown below. You will see this style of coding used quite extensively in the COM programming community.

 int main(int argc, char* argv[])  {   ISVideo* pSVideo = NULL;    VRESULT vr = CreateInstance("vcr.dll", "svideo",      reinterpret_cast<IGeneral**>(&pSVideo));    if (V_FAILED(vr)) {     ReportError(vr);      return 1;    }    UseSVideo(pSVideo);    pSVideo->Delete();    return 0;  } 

Let s summarize what we have achieved so far.

We took a monolithic application and broke it into two components with a desire to field-replace a buggy component or to reuse a third-party component. In the process we:

  1. separated interface from implementation

  2. added mechanism to dynamically load a component

  3. defined a general interface that formed the base of all other interfaces

  4. gave the server the responsibility of managing the lifetime of an object

  5. gave the client the responsibility to cooperate with the server in managing the lifetime of the object

  6. defined a simple reference counting mechanism in order to reduce the complexity of lifetime management

  7. added some optimization on obtaining the initial interface pointer from the server

  8. implemented an error-reporting mechanism


COM+ Programming. A Practical Guide Using Visual C++ and ATL
COM+ Programming. A Practical Guide Using Visual C++ and ATL
ISBN: 130886742
Year: 2000
Pages: 129 © 2008-2017.
If you may any questions please contact us: