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:

C++
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 calling throw *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:

C++
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:

C++
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:

C++
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.

See Also