The Database Managers, Inc.

Contact The Database Managers, Inc.


Use an RSS enabled news reader to read these articles.Use an RSS enabled news reader to read these articles.

Defining an Exception using a Macro

by Curtis Krauskopf

I'm a lazy coder. I don't like writing more code than I have to and I don't like to see redundancy.

Last week, I was working on a C++ class for a new library. Because the class was going to be used by coders other than myself, I paid particular attention to the ways that the class was designed to recover from errors.

The constructor for the C++ class I was building was particularly robust and there were several ways that it could fail. A long time ago, I learned one of the best ways to handle an error in a class (especially in a constructor) is to throw an exception. I created an exception class for each failure -- and in the process, I ran across a lot of redundancy. Figure 1 shows the exception classes I coded for one class in the library.

...
class FileNotFoundError : public exception
{ 
public:
  FileNotFoundError() 
      : exception("File not found")
  { }
};

class NoColumnLabelsError : public exception
{ 
public:
  NoColumnLabelsError() 
      : exception("No column labels")
  { }
};

class ColumnNotFoundError : public exception
{ 
public:
  ColumnNotFoundError() 
      : exception("Column not found")
  { }
};
...
Figure 1: Three exception definitions

In figure 1, I define three exceptions:

  • FileNotFoundError
  • NoColumnLabelsError
  • ColumnNotFoundError

The exceptions all inherit from the exception class. In the standard C++ library, the exception class is available by #include-ing <exception>, like this:

#include <exception>

The definition of each exception is almost trivial. An exception constructor takes a char * message parameter. The message helps to distinguish, in the catch() block at runtime, which exception was triggered.

The compiler I was using was the Microsoft Visual C++ .NET compiler. Their implementation of the exception class allows a const char * to be passed to the class' constructor, as shown in the constructor for the FileNotFoundError class in figure 1:

FileNotFoundError() 
    : exception("Input file not found") 

Overall, these three exceptions take 21 lines of code. That's a lot to look at. It's also a nightmare for the maintenance programmer because the first question he/she will ask is "what's the same in these three exceptions and is there anything special defined in any one of them?". Even though the definition of each exception is relatively simple, it's still a lot for a maintenance programmer to digest. Another issue: this code needs to be repeated for each exception and my design plans on having dozens of exceptions throughout the library.

There must be a better solution.

I'm normally not in favor of macros in C++ programming. Many books, magazine articles and web pages have influenced my opinion on this. But in this case, a macro will certainly simplify the coding. Overall, I dislike redundancy more than I dislike macros.

The solution I'll use will turn the exceptions in figure 1 into the simple lines of code in figure 2.

DEFINE_EXCEPTION(FileNotFoundError, "File not found");
DEFINE_EXCEPTION(NoColumnLabelsError, "No column labels");
DEFINE_EXCEPTION(ColumnNotFoundError, "Column not found");
Figure 2: Three improved exception definitions

These exception definitions are much easier to understand. Two side-effects of this implementation are:

  1. The maintenance programmer knows that each exception is defined the same way
  2. Changes to the DEFINE_EXCEPTION macro will affect all of the exception definitions

The definition for the DEFINE_EXCEPTION macro for a Microsoft Visual C++ implementation of the exception class is in figure 3.

define_exception.h:     (For the MicroSoft Visual C++ .NET compiler)
#ifndef DEFINE_EXCEPTION_H
#define DEFINE_EXCEPTION_H
#include <exception>
// DEFINE_EXCEPTION macro defines exceptions derived from the
// standard exception class.
//
// Copyright (c) 2006 by Curtis Krauskopf
//                       >>http://www.decompile.com
//                                                               |
// Permission to use, copy, modify, distribute and sell this 
//     software for any purpose is hereby granted without fee, 
//     provided that the above copyright notice appears in all 
//     copies of this code and in all modified versions of this 
//     code.
// The author makes no representations about the suitability of 
//     this software for any purpose. It is provided "as is" 
//     without express or implied warranty.
//
// Created:  July 20, 2006 by Curtis Krauskopf (cdk)
// 
// In the parameter list:
//    ClassName is the exception being defined.  It should not 
//    have been previously defined in the current namespace.
//    Message is the message that will be returned by calling 
//    the .what() method.
//    Message can be "".
//    The exception's type is the ClassName.
//  
#define DEFINE_EXCEPTION(ClassName,Message) \
    class ClassName : public exception \
    { \
    public: \
        ClassName(const char *msg = Message) \
            : exception(msg) \
        { } \
    };
#endif
Figure 3: Exception definition macro

As demonstrated by the example in figure 2, the DEFINE_EXCEPTION macro takes two parameters:

Parameter Definition
ClassName The name of the exception class being defined
Message A message that is available at runtime by calling the .what() method on the exception object

As mentioned above, the Microsoft Visual C++ .NET compiler's exception class allows a const char * to be passed to the class' constructor. Not all compilers have this capability -- notably the Borland C++ Builder compiler.

Figure 4 shows an implementation of the DEFINE_EXCEPTION macro for compilers that don't have a const char * exception constructor. It's also compatible with the Microsoft Visual C++ .NET compiler because of the way the local const char * for the message pointer is handled.

define_exception.h:      (Suitable for all compilers)
Download define_exception.h (2.5k)
#ifndef DEFINE_EXCEPTION_H
#define DEFINE_EXCEPTION_H
#include <exception>
// DEFINE_EXCEPTION macro defines exceptions derived from the
// standard exception class.
//
// Copyright (c) 2006 by Curtis Krauskopf
//                       >>http://www.decompile.com
//                                                               |
// Permission to use, copy, modify, distribute and sell this 
//     software for any purpose is hereby granted without fee, 
//     provided that the above copyright notice appears in all 
//     copies of this code and in all modified versions of this 
//     code.
// The author makes no representations about the suitability of 
//     this software for any purpose. It is provided "as is" 
//     without express or implied warranty.
//
// Created:  July 20, 2006 by Curtis Krauskopf (cdk)
// Update: July 24, 2006: consolidate MSVC solution
//     with Borland C++ Builder and GNU solution.
//
// 
// For the declaration:  
//   DEFINE_EXCEPTION(FileNotFoundError, "File not found");
//
// the macro expands to:
//
//  class FileNotFoundError : public ExceptionHelper
//  {
//  public:
//      FileNotFoundError(const char *msg = "File not found")
//          : ExceptionHelper(msg)
//      { }
//  };
//
// In the parameter list:
//    ClassName is the exception being defined.  It should not 
//    have been previously defined in the current namespace.
//    Message is the message that will be returned by calling 
//    the .what() method.
//    Message can be "".
//    The exception's type is the ClassName.
//    
//    Class heirarchy:
//
//          std::exception
//                ^
//                |
//          ExceptionHelper
//                ^
//                |
//          ClassName (class being defined)
//          


// ExceptionHelper augments the standard exception class by
// allowing a const char * parameter in the constructor.
class ExceptionHelper : public std::exception
{
public:
  ExceptionHelper(const char *msg)
    : std::exception(), msg_(msg)
    { }
  virtual const char * what() const throw() { return msg_; }
private:
  const char *msg_;
};

#define DEFINE_EXCEPTION(ClassName,Message) \
    class ClassName : public ExceptionHelper \
    { \
    public: \
        ClassName(const char *msg = Message) \
            : ExceptionHelper(msg) \
        { } \
    };

#endif
Figure 4: DEFINE_EXCEPTION macro definition that is suitable for all C++ compilers.

In figure 4, the ExceptionHelper class augments the std::exception class by allowing a const char * parameter in the constructor. At runtime, the what() method allows access to the message that was passed in the constructor.

The test program for the DEFINE_EXCEPTION macro is the same program for both figures 3 and 4. The source code for the test program is in figure 5.

test_define_exception.cpp:
#include <iostream>

#include "exception_definition.h"

int main(int, char* )
{
  std::cout << "testing..." << std::endl;
  DEFINE_EXCEPTION(FileNotFoundError, "FileNotFoundError")
  try {
    throw FileNotFoundError();
  }
  catch(FileNotFoundError &e) {
    std::cout << "Caught: " << e.what() << std::endl;
  }

  try {
    throw FileNotFoundError("Custom Exception Text");
  }
  catch(FileNotFoundError &e) {
    std::cout << "Caught: " << e.what() << std::endl;
  }

  return 0;
}
Figure 5: Test program that demonstrates the DEFINE_EXCEPTION macro.

The output for the test program is shown in figure 6.

testing...
Caught: FileNotFoundError
Caught: Custom Exception Text
Figure 6: Test program that demonstrates the DEFINE_EXCEPTION macro.

Conclusion:

Language features that are convenient to use will be used more often. The implementation of the DEFINE_EXCEPTION macro makes exception creation almost trivial.

Curtis Krauskopf is a software engineer and the president of The Database Managers (www.decompile.com). He has been writing code professionally for over 25 years. His prior projects include multiple web e-commerce applications, decompilers for the DataFlex language, aircraft simulators, an automated Y2K conversion program for over 3,000,000 compiled DataFlex programs, and inventory control projects. Curtis has spoken at many domestic and international DataFlex developer conferences and has been published in FlexLines Online, JavaPro Magazine, C/C++ Users Journal and C++ Builder Developer's Journal.

Copyright 2003-2010 The Database Managers, Inc.

Popular C++ topics at The Database Managers:

Services | Programming | Contact Us | Recent Updates
Send feedback to: