3

I often want to define new 'Exception' classes, but need to have an appropriate constructor defined because constructors aren't inherited.

class MyException : public Exception 
{ 
public:
  MyException (const UString Msg) : Exception(Msg)
  {
  };
}

Typedefs don't work for this, because they are simply aliases, not new classes. Currently, to avoid repeating this trivial boilerplate, I use a #define which does the donkeywork.

#define TEXCEPTION(T) class T : public Exception \
{ \
public:\
    T(const UString Msg) : Exception(Msg) {}; \
}

...

TEXCEPTION(MyException);

But I keep wondering if there's a better way of achieving this - maybe with templates, or some new C++0x feature

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • It may be a good idea to derive from std::exception. I generally use std::runtime_error. Check out stdexcept – Martin York Jan 02 '09 at 22:35
  • That's a Delphi vs. C++ issue. Because this is primarily VCL-based code, I'm using their exception hierarchy. – Roddy Jan 02 '09 at 22:44

3 Answers3

4

If you really want to have new classes derived from Exception, as opposed to having a template parameterized by a parameter, there is no way around writing your own constructor that just delegates the arguments without using a macro. C++0x will have the ability what you need by using something like

class MyException : public Exception 
{ 
public:
    using Exception::Exception;
};

You can read about the details of that (seem to have quite a bit of extra rules) in 12.9 "Inheriting Constructors" in the latest draft of C++0x.

In the meantime, i would recommend a policy based design (made small text, because the OP accepted the above, and not this policy stuff):

// deriving from Impl first is crucial, so it's built first
// before Exception and its Ctor can be used.
template<typename Impl>
struct ExceptionT : Impl, Exception {
    // taking a tuple with the arguments.
    ExceptionT(arg_types const& t = arg_types())
        :Exception(Impl::Ctor(t)) { }
    // taking a string. plain old stuff
    ExceptionT(std::string const& s):Exception(Impl::Ctor(s)) { }
};

struct ExceptionDefImpl {
    typedef boost::tuple<> arg_types;

    // user defined ctor args can be done using a tuple
    std::string Ctor(arg_types const& s) {
        return std::string();
    }

    std::string const& Ctor(std::string const& s) {
        return s;
    }
};

// will inherit Ctor modifier from DefImpl.
struct MemoryLost : ExceptionDefImpl { 
    typedef boost::tuple<int> arg_types;

    std::string Ctor(arg_types const& s) {
        std::ostringstream os;
        os << "Only " << get<0>(s) << " bytes left!";
        return os.str();
    }

    int getLeftBytes() const { return leftBytes; }
private:
    int leftBytes;
};

struct StackOverflow : ExceptionDefImpl { };

// alias for the common exceptions
typedef ExceptionT<MemoryLost> MemoryLostError;
typedef ExceptionT<StackOverflow> StackOverflowError;

void throws_mem() {
    throw MemoryLostError(boost::make_tuple(5));
}    

void throws_stack() { throw StackOverflowError(); }

int main() {
    try { throws_mem(); } 
    catch(MemoryListError &m) { std::cout << "Left: " << m.getLeftBytes(); }
    catch(StackOverflowError &m) { std::cout << "Stackoverflow happened"; }
}

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I am not sure how this solves the original problem. The difference is the you define Ctor rather than the constructor. – Martin York Jan 02 '09 at 22:35
  • I liked this answer better without the policy/boost/stringstream stuff. Now I'm just confused :-s "In the meantime", I'll be sticking to #define. – Roddy Jan 02 '09 at 22:42
  • no, i don't. you don't have to define Ctor Martin. as you see with StackOverflowError, you can get away with just "struct StackOverflow : ExceptionDefImpl { }; typedef ExceptionT StackOverflowError; and have a string taking ctor. – Johannes Schaub - litb Jan 02 '09 at 23:38
  • i'm sorry Roddy if it confused you :/ i'll make it .. so it's smaller :) – Johannes Schaub - litb Jan 02 '09 at 23:39
  • @litb: OK, I admit I enjoy being confused by your answers when I don't understand them - it makes me dig deeper and learn more! – Roddy Jan 03 '09 at 20:28
1

You could parameterize your template class with an integer:

#include <iostream>
#include <string>

using namespace std;

enum ExceptionId {
    EXCEPTION_FOO,
    EXCEPTION_BAR
};

class Exception {
    string msg_;

public:
    Exception(const string& msg) : msg_(msg) { }
    void print() { cout << msg_ << endl; }
};

template <int T>
class TException : public Exception {
public:
    TException(const string& msg) : Exception(msg) {};
};

void
foo()
{
    throw TException<EXCEPTION_FOO>("foo");
}

void
bar()
{
    throw TException<EXCEPTION_BAR>("bar");
}

int
main(int argc, char *argv[])
{
    try {
        foo();
    } catch (TException<EXCEPTION_FOO>& e) {
        e.print();
    };

    try {
        bar();
    } catch (TException<EXCEPTION_BAR>& e) {
        e.print();
    };

    return 0;
}

Although, I don't see why you would favor this over using a single class with an internal enumeration that is set/read at runtime:

class TException {
public:
    enum Type { FOO, BAR };

    TException(Type type, const string& msg) : Exception(msg), type_(type) {}

    Type type() const { return type_; }

private:
    Type type_;
};

Then just switch on the type when you catch a TException...

Judge Maygarden
  • 26,961
  • 9
  • 82
  • 99
  • The enum list is the bit I don't like :( - Each module creates/uses it's own exception, and a central 'list' of those would be a PITA to maintain. A bit like error numbers ;-) ... Now, if I could write "template " I'd be happier. typedef TException<"FOO"> FooException; – Roddy Jan 02 '09 at 20:44
  • what about using tag types? struct Foo; typedef TException FooException; – Johannes Schaub - litb Jan 02 '09 at 20:52
  • What's the difference between maintaining the enum list and maintaining the macro calls? – Judge Maygarden Jan 02 '09 at 20:53
  • macro calls can be spread out over many source files - no coupling. Exception types have appropriate local scope if needed. But - Enum list must be in one place to avoid duplication - I think? – Roddy Jan 02 '09 at 21:03
  • Why not derive from a standard exception. std::runtime_error. They use what() – Martin York Jan 02 '09 at 22:32
1
// You could put this in a different scope so it doesn't clutter your namespaces.
template<struct S>   // Make S different for different exceptions.
class NewException :
    public Exception 
{ 
    public:
        NewException(const UString Msg) :
            Exception(Msg)
        {
        }
};

// Create some new exceptions
struct MyExceptionStruct;    typedef NewException<MyExceptionStruct> MyException;
struct YourExceptionStruct;  typedef NewException<YourExceptionStruct> YourException;
struct OurExceptionStruct;   typedef NewException<OurExceptionStruct> OurException;

// Or use a helper macro (which kinda defeats the purpose =])
#define MAKE_EXCEPTION(name) struct name##Struct; typedef NewException<name##Struct> name;

MAKE_EXCEPTION(MyException);
MAKE_EXCEPTION(YourException);
MAKE_EXCEPTION(OurException);

// Now use 'em
throw new MyException(":(");
strager
  • 88,763
  • 26
  • 134
  • 176
  • that won't work mate. string literals have internal linkage (won't be exported), and are not guaranteed to yield the same address when used multiple times. a pointer template nontype parameter, however, requires the address of an identifier with external linkage. i miss string template args too tho – Johannes Schaub - litb Jan 02 '09 at 20:55
  • @litb, Ah, I completely forgot about that issue. Do you think using std::string (or UString) would work? – strager Jan 02 '09 at 20:58
  • the only way to make it work is: extern const char name[] = "MyException"; typedef NewException MyException; (it won't even work within a single translation unit using the literal. teh std forbids it). maybe with some preprocessor trickery and #T trick you can do it in one line :) – Johannes Schaub - litb Jan 02 '09 at 21:15
  • Well, I've updated my answer to give it another shot. Looks kinda messy, though... – strager Jan 02 '09 at 21:22