Table of Contents Previous Next
Logo
Client-Side Slice-to-C++ Mapping : 6.9 Mapping for Exceptions
Copyright © 2003-2008 ZeroC, Inc.

6.9 Mapping for Exceptions

Here is a fragment of the Slice definition for our world time server from Section 4.10.5 on page 115 once more:
exception GenericError {
    string reason;
};
exception BadTimeVal extends GenericError {};
exception BadZoneName extends GenericError {};
These exception definitions map as follows:
class GenericError: public Ice::UserException {
public:
    std::string reason;

    GenericError() {}
    explicit GenericError(const string&);

    virtual const std::string&  ice_name() const;
    virtual Ice::Exception*     ice_clone() const;
    virtual void                ice_throw() const;
    // Other member functions here...
};

class BadTimeVal: public GenericError {
public:
    BadTimeVal() {}
    explicit BadTimeVal(const string&);

    virtual const std::string&  ice_name() const;
    virtual Ice::Exception*     ice_clone() const;
    virtual void                ice_throw() const;
    // Other member functions here...
};

class BadZoneName: public GenericError {
public:
    BadZoneName() {}
    explicit BadZoneName(const string&);

    virtual const std::string&  ice_name() const;
    virtual Ice::Exception*     ice_clone() const;
    virtual void                ice_throw() const;
};
Each Slice exception is mapped to a C++ class with the same name. For each exception member, the corresponding class contains a public data member. (Obviously, because BadTimeVal and BadZoneName do not have members, the generated classes for these exceptions also do not have members.)
The inheritance structure of the Slice exceptions is preserved for the generated classes, so BadTimeVal and BadZoneName inherit from GenericError.
Each exception has three additional member functions:
• ice_name
As the name suggests, this member function returns the name of the exception. For example, if you call the ice_name member function of a BadZoneName exception, it (not surprisingly) returns the string "BadZoneName". The ice_name member function is useful if you catch exceptions generically and want to produce a more meaningful diagnostic, for example:
try {
    // ...
} catch (const Ice::GenericError& e) {
    cerr << "Caught an exception: " << e.ice_name() << endl;
}
If an exception is raised, this code prints the name of the actual exception (BadTimeVal or BadZoneName) because the exception is being caught by reference (to avoid slicing).
• ice_clone
This member function allows you to polymorphically clone an exception. For example:
try {
    // ...
} catch (const Ice::UserException& e) {
   Ice::UserException* copy = e.clone();
} 
ice_clone is useful if you need to make a copy of an exception without knowing its precise run-time type. This allows you to remember the exception and throw it later by calling ice_throw.
• ice_throw
ice_throw allows you to throw an exception without knowing its precise run-time type. It is implemented as:
void
GenericError::ice_throw() const
{
    throw *this;
}
You can call ice_throw to throw an exception that you previously cloned with ice_clone.
Each exception has a default constructor. This constructor performs memberwise initialization; for simple built‑in types, such as integers, the constructor performs no initialization, whereas complex types, such as strings, sequences, and dictionaries are initialized by their respective default constructors.
An exception also has a second constructor that accepts one argument for each exception member. This constructor allows you to instantiate and initialize an exception in a single statement, instead of having to first instantiate the exception and then assign to its members. For derived exceptions, the constructor accepts one argument for each base exception member, plus one argument for each derived exception member, in base-to-derived order.
Note that the generated exception classes contain other member functions that are not shown on page 200. However, those member functions are internal to the C++ mapping and are not meant to be called by application code.
All user exceptions ultimately inherit from Ice::UserException. In turn, Ice::UserException inherits from Ice::Exception (which is an alias for IceUtil::Exception):
namespace IceUtil {
    class Exception {
        virtual const std::string& ice_name() const;
        Exception*                 ice_clone() const;
        void                       ice_throw() const;
        virtual void               ice_print(std::ostream&) const;
        // ...
    };
    std::ostream& operator<<(std::ostream&, const Exception&);
    // ...
}

namespace Ice {
    typedef IceUtil::Exception Exception;

    class UserException: public Exception {
    public:
        virtual const std::string& ice_name() const = 0;
        // ...
    };
}
Ice::Exception forms the root of the exception inheritance tree. Apart from the usual ice_name, ice_clone, and ice_throw member functions, it contains the ice_print member functions. ice_print prints the name of the exception. For example, calling ice_print on a BadTimeVal exception prints:
BadTimeVal
To make printing more convenient, operator<< is overloaded for Ice::Exception, so you can also write:
try {
    // ...
} catch (const Ice::Exception& e) {
    cerr << e << endl;
}
This produces the same output because operator<< calls ice_print internally.
For Ice run time exceptions, ice_print also shows the file name and line number at which the exception was thrown.
Table of Contents Previous Next
Logo