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
.