Journal Articles

CVu Journal Vol 15, #2 - Apr 2003 + Programming Topics
Browse in : All > Journals > CVu > 152 (9)
All > Topics > Programming (877)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Non-Standard Code

Author: Administrator

Date: 03 April 2003 13:15:56 +01:00 or Thu, 03 April 2003 13:15:56 +01:00

Summary: 

I recently faced a problem which I felt I could only solve using non-standard code, and thought that some of the issues this brought up might be of interest to the readers of CVu.

Body: 

I recently faced a problem which I felt I could only solve using non-standard code, and thought that some of the issues this brought up might be of interest to the readers of CVu.

Although the example is using the Microsoft compiler, the principles will hopefully be of general interest.

Firstly, what was my issue?

I'm writing C++ code which uses exceptions, and using some libraries which can also throw exceptions. Unfortunately it seems to be hard to provide useful information when exceptions occur, unless you know the types of exceptions which might be thrown; and as in this case there were several, unrelated, exception hierarchies involved as well as character literals this was not easy to discover.

A common idiom in standard C++ for such cases is to have a 'generic' exception handler function which is called from a catch statement. This simply re-throws the exception within another try/catch block where a catch handler is written for each known type. An extremely simple example of this idiom follows:

// Called from within a catch handler to log
// a string from the current exception
void logException() {
  std::string error;
  try {
    // re-throw the exception to have another
    // look at it
    throw; 
  }

  catch(std::exception & ex) {
    error = ex.what();
  }
  catch(CException * pEx) {
    static char szMsg[255];
    pEx->GetErrorMessage(szMsg,
                         sizeof(szMsg));
    pEx->Delete();
    error = szMsg;
  }
  // etc etc
  catch(const char *pStr) {
    error = pStr;
  }
  catch(...) {
    // Search me...
    throw;
  }
  std::cerr << "Uncaught exception: "
            << error << std::endl;
}
// Example of use
int main(int argc, char **argv) {
  try {
    return realmain(argc, argv);
  }
  catch(...) {
    logException();
    return EXIT_FAILURE; 
  }
}

There were two problems with this approach. The first was that the idiom does not work in my current environment (Microsoft VC 6.0) because the destructor is called twice for re-thrown exceptions. This bug appears to have finally gone in VC.NET, but I need to continue supporting VC 6. The best work around for this bug is to ensure all destructors for exception objects can be called twice, but since I didn't write all the exception objects this was not an option.

The second problem with this approach is that it only works where you know the complete list of exceptions which can be thrown. Given the lack of documentation which seems endemic in our industry it is all too easy to miss one.

What happens then depends on your runtime. The C++ standard simply says: "If no matching handler is found in a program, the function terminate() is called; whether or not the stack is unwound before this call to terminate() is implementation defined".

My environment produces the message "Abnormal program termination" which provides little help in finding the root cause of the problem.

I decided that the lack of useful information meant I was spending too much time debugging problems and so I was prepared to consider using some non-standard code to help me.

It is obvious from how the exception mechanism works that the runtime must know the type of the thrown object so it can match it to the appropriate catch handler. I decided that if I could get the runtime type of the exception many of my problems would be over.

Unfortunately this is not (yet) possible in standard C++. there have been a few discussions I have read over the years but, as far as I know, there still isn't a concrete proposal to provide this feature.

Of course at this point any Java or C# programmers reading this article are proably feeling pretty smug, since these languages (for a variety of sound reasons), provide significantly more information about exceptions than C++ does.

After some "poking around" in the compiler output and referring to some articles in MSJ by Matt Peitrek I discovered that I could write some Microsoft specific code to catch the underlying Win32 exception used to implement Microsoft's exception handling, and decode it to get hold of the type_info for the thrown exception.

So my new, highly non-portable code, looks like this:

#include <windows.h>
#include <typeinfo>
#include <iostream>

// Microsoft specific code
DWORD logException(_EXCEPTION_POINTERS*
                               pException) {
  // exception code for MSVC C++ exception
  static const int
              MsvcExceptionCode(0xe06d7363);
  // see EXSUP.INC for only public definition
  static const int MagicNumber1(0x19930520);

  PEXCEPTION_RECORD ExceptionRecord =
                pException->ExceptionRecord;
  if( (ExceptionRecord->ExceptionCode
                    == MsvcExceptionCode) &&
      (ExceptionRecord->NumberParameters
                                    == 3) &&
      (ExceptionRecord->ExceptionInformation[0]
                        == MagicNumber1) ) {
    struct link {
      link *chain;
    };

    // Hackery to get the type_info buried
    // in the data
    link *p = (link*)ExceptionRecord->
                    ExceptionInformation[2];
    p = p[3].chain;
    p = p[1].chain;
    const std::type_info *t =
       (const std::type_info *)(p[1].chain);

    std::cerr << "Uncaught exception: "
              << t->name() << std::endl;

    return EXCEPTION_EXECUTE_HANDLER;
  }
  return EXCEPTION_CONTINUE_SEARCH;
}

and the corresponding sample usage is:

#include <excpt.h>

int main(int argc, char **argv) {
  __try {
    return realmain(argc, argv);
  }
  __except(logException(exception_info())) {}
  return 0;
}

However, I have gone from the original code which used standard C++ features to some code which uses several Microsoft specific calls and, even worse, a number of undocumented 'magic numbers' and pointer hacks.

This raises a few questions, and motivated me to write this article.

  1. Is it worth it?

  2. How do I encapsulate the trickery?

  3. What can I do about maintainability?

Many people may feel the first question is all that needs asking, and answering with a resounding NO. However, in this case I felt that the gain for me of getting out the name of the object being thrown was greater than the ugliness of the solution. This is a judgement call, and each problem like this presents different trade-offs.

There is also a separation between code used for development and code shipped in production systems. I would be much less happy to ship a product which relied on this trick for normal operation, but if something goes wrong handling an uncaught exception I'm not really much worse off.

Encapsulating the 'trickery' is important for two reasons.

Firstly, I want to be able to write some test harnesses for the trick code which can be run whenever an upgrade is installed, or other compiler settings are used, etc.

Secondly, if I find the code fails in some as yet undiscovered way I want to have a single place to fix.

Thirdly, I want to hide the code so I can 'forget' this dangerous knowledge.

Hence I want to write this function inside a separate C++ source file. I would also add checks for the preprocessor symbol for the compiler to check it matches one of the versions I have tested the code for. This ensures I explicitly check the code when I move to a different compiler level, and also prevents me accidently trying to use this code with a non-Microsoft (in this case) compiler.

The big problem with non-portable code is maintainability. This is a problem for many reasons.

First of all I have used undocumented features of the compiler and so my chances of support if anything goes wrong is almost zero. Since I am using such features I am relying on my guesses about the structures being right. It is quite likely my guesses are incomplete, causing the code to break under some conditions.

Then I have a vulnerability to the future - I am at the mercy of the compiler writers who may well change this sort of internal detail with a subsequent release of the compiler (or even with a patch release). Will it be possible for me to change my trick to cater for the future structures? Will I still be around to do this work by then, and if not is there anyone else who could?

The best I can do to reduce this vulnerability is to ensure the function is documented to use non-portable code and provide some documentation about the 'detective work' which went in to finding this solution. A good set of test cases exercising the function in as wide a variety of cases as possible and compiled with as many versions of the compiler I can find helps greatly with ensuring the robustness of the solution and also provides a ready made test bed for future compiler releases.

Having made the function a separate compilation unit, as described above, also makes it easier to write stand-alone test cases. I hope this quick run through the issues I thought about when writing this non-standard piece of code may help you when you find yourself facing a problem which you can't seem to fix using the standard set of features.

Notes: 

More fields may be available via dynamicdata ..