exception GenericError {
string reason;
};
exception BadTimeVal extends GenericError {};
exception BadZoneName extends GenericError {};
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.)
try {
// ...
} catch (const Ice::GenericError& e) {
cerr << "Caught an exception: " << e.ice_name() << endl;
}
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 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 197. 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:
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.
Exceptions have a default constructor that default-constructs each data member. Members having a complex type, such as strings, sequences, and dictionaries, are initialized by their own default constructor. However, the default constructor performs no initialization for members having one of the simple built‑in types boolean, integer, floating point, or enumeration. For such a member, it is not safe to assume that the member has a reasonable default value. This is especially true for enumerated types as the member’s default value may be outside the legal range for the enumeration, in which case an exception will occur during marshaling unless the member is explicitly set to a legal value.
If you wish to ensure that data members of primitive types are initialized to reasonable values, you can declare default values in your Slice definition (see
Section 4.10.2). The default constructor initializes each of these data members to its declared value.
Exceptions also have a second constructor that has one parameter for each data member. This allows you to construct and initialize a class instance in a single statement (instead of first having to construct the instance and then assigning to its members). For derived exceptions, this constructor has one parameter for each of the base class’s data members, plus one parameter for each of the derived class’s data members, in base-to-derived order.