Intercepting User Exception Insertion and Extraction in C++
Inserting a User Exception in C++
As in the case of Ice objects, a Dynamic Ice application may represent user exceptions in a native format that is not directly compatible with the Ice API. If the application needs to raise such a user exception to the Ice run time (so that it will be marshaled and sent back to the client), the exception must be wrapped in a subclass of Ice::UserException. The Dynamic Ice API provides a class to simplify this process:
namespace Ice {
class UserExceptionWriter : public UserException {
public:
UserExceptionWriter(const CommunicatorPtr&);
virtual void write(const OutputStreamPtr&) const = 0;
virtual std::string ice_name() const = 0;
virtual Ice::Exception* ice_clone() const = 0;
virtual void ice_throw() const = 0;
// ...
};
typedef ... UserExceptionWriterPtr;
}
A subclass of UserExceptionWriter is responsible for supplying a communicator to the constructor, and for implementing the following methods:
void write(const OutputStreamPtr&) const
This method is invoked when the Ice run time is ready to marshal the exception. The subclass must marshal the exception using the encoding rules for exceptions.
std::string ice_name() const
Return the Slice name of the exception.
Ice::Exception* ice_clone() const
Return a copy of the exception.
void ice_throw() const
Raise the exception by callingthrow *this.
Extracting a User Exception in C++
An application extracts a user exception by calling one of two versions of the throwException method defined in the InputStream class:
namespace Ice {
class InputStream : ... {
public:
virtual void throwException() = 0;
virtual void throwException(const UserExceptionReaderFactoryPtr&) = 0;
// ...
};
}
The version without any arguments attempts to locate and throw a C++ implementation of the encoded exception, relying on statically-generated type information emitted by the Slice-to-C++ compiler.
If your goal is to create an exception in another type system, such as a native PHP exception object, you must call the second version of throwException and pass an implementation of UserExceptionReaderFactory:
namespace Ice {
class UserExceptionReaderFactory : public IceUtil::Shared {
public:
virtual void createAndThrow(const std::string& typeId) const = 0;
};
typedef ... UserExceptionReaderFactoryPtr;
}
As the stream iterates over slices of an exception from most-derived to least-derived, it invokes createAndThrow passing the type ID of each slice, giving the application an opportunity to raise an instance of UserExceptionReader:
namespace Ice {
class UserExceptionReader : public UserException {
public:
UserExceptionReader(const CommunicatorPtr&);
~UserExceptionReader() throw();
virtual void read(const InputStreamPtr&) const = 0;
virtual std::string ice_name() const = 0;
virtual UserException* ice_clone() const = 0;
virtual void ice_throw() const = 0;
protected:
const CommunicatorPtr _communicator;
};
}
Subclasses of UserExceptionReader must implement the pure virtual functions. In particular, the implementation of read must call InputStream::startException, unmarshal the remaining slices, and then call InputStream::endException.