Q: Exception design questions

Myron Alexander someone at somewhere.com
Fri Jun 15 19:32:02 PDT 2007


Hello.

I am struggling with creating an exception design for my library. I have 
written libraries and exception mechanisms before but those libraries 
were focused on a core problem for which all use-cases are known 
upfront. With this library, because it is distributed, I am not in 
control of how it is used so I am struggling to envision all possibilities.

What I have decided is to restrict the number of exceptions to just 
those that either define a domain or declare a specific recoverable 
exception. For my library, there will be very few recoverables, mostly 
to do with data conflicts and database locks.

I am of the type that, as a library/framework architect, tries to 
provide the most information possible to the developer such that the 
developer is able to quickly pinpoint the problem and resolve it.

With that in mind, I needed a flexible exception class that could handle 
all the information known about the state at the time of the exception. 
I decided on a property bag that will hold all the information as 
properties.

This is what I came up with:

> public class SqlException : Exception {
> 
>    this (char[] msg) {
>       super (msg);
>    }
> 
>    typeof(this) setSql (char[] sql) {
>       m_propertyBag[K_SQL] = box (sql);
>       return this;
>    }
> 
>    typeof(this) setProperty(T) (char[] property, T value) {
>       m_propertyBag [property] = box (value);
>       return this;
>    }
> 
>    void raise () {
>       throw this;
>    }
> 
>    char[] toString () {
> 
>       /* Format properties for output. The properties are listed one to a line
>        * of format 'name: value'.
>        */
>       char[] pstr;
> 
>       foreach (p; m_propertyBag.keys.sort) {
>          pstr ~= "\n" ~ p ~ ": " ~ m_propertyBag[p].toString ();
>       }
> 
>       if (pstr.length > 0) {
>          return msg ~ "\n" ~ pstr;
> 
>       } else {
>          return msg;
>       }
>    }
> 
>    char[] sql () {
>       if (K_SQL in m_propertyBag) {
>          return unbox!(char[])(m_propertyBag[K_SQL]);
> 
>       } else {
>          return null;
>       }
>    }
> 
>    Box[char[]] propertyBag () {
>       return m_propertyBag;
>    }
> 
>    protected Box[char[]] m_propertyBag;
>    
>    private static const final K_SQL = "SQL";
> }

And this is how I use it:

> (new SqlProgrammingException (
>    "Invalid bind type. Mixing single and multiple value rows. "
>    "The first argument type is other than Box[], thus it is "
>    "assumed that the rest of the arguments are single value "
>    "rows."))
> .setSql (operation)
> .setProperty ("ValueRow", i)
> .setProperty ("ValueType", t)
> .raise ();

which outputs:

> Error: Invalid bind type. Mixing single and multiple value rows. The first
> argument type is other than Box[], thus it is assumed that the rest of the
> arguments are single value rows.
> 
> SQL: insert into atable values (?,?,?)
> ValueRow: 1
> ValueType: std.boxer.Box[]

My questions are:

1. Is there a better way to design the exceptions?
2. If you have experience with this style what shortcomings did you notice.
3. Is there anything I have missed (conceptually or practically)?
4. Is there anything I should remove, or add?

Thanks ahead,

Myron.



More information about the Digitalmars-d mailing list