# Augmented exceptions

**URL:** <https://www.ruby-forum.com/t/augmented-exceptions/133995>\
**Category:** Ruby\
**Created:** [April 6, 2008, 8:48am UTC](https://www.ruby-forum.com/t/augmented-exceptions/133995 "2008-04-06T08:48:27Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Joel\_VanderWerf](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@Joel\_VanderWerf](https://www.ruby-forum.com/u/Joel_VanderWerf)\
**Post date:** [April 6, 2008, 8:48am UTC](https://www.ruby-forum.com/t/augmented-exceptions/133995/1 "2008-04-06T08:48:27Z")

</div>

I was working with exceptions bubbling up out of some C code, and having  
trouble crafting an exception message that was descriptive enough to  
identify the object(s) involved so I could easily find and inspect them.

The following is some ruby and C code (the ruby code is useful by  
itself) for “augmented exceptions”. Maybe there’s better terminology,  
but the idea is that you can tell the handler which object is causing  
the problem, or if there is no handler then at least the stack dump  
tells you a little more about the object. It falls back gracefully if  
#inspect (or #to\_s) fails for the object in question.

========================================================================

# Include this module in an exception class to pass an object along

# with the exception so that it can be used by the rescuer. To raise

# an exception with an object, use this syntax:

# 

# raise YourExceptionClass, [message\_string, object], …

# 

# To raise an exception normally, with just a message string:

# 

# raise YourExceptionClass, message\_string, …

# 

# The object is used in two ways.

# 

# First, the message you provide when you raise the exception is

# concatenated with a string describing the object so that the message

# string on the receiving end of the exception says something helpful

# about this object. To generate the string, we first use #inspect on

# the object. If #inspect fails, we fall back to #to\_s; if that fails,

# we fall back to class name and #object\_id.

# 

# Second, the object can be accessed from $! (or, equivalently, the

# exception object captured using the “rescue =\> ex” syntax) using

# the #object method.

# 

# The rationale for all of this is that sometimes you want to be able

# to do “post-mortem” analysis on an object involved in an exception,

# either in a rescue clause or by looking at the exception message.

# 

module AugmentedException  
attr\_reader :object

def initialize(msg)  
if defined?(msg.first)  
msg, @object = \*msg  
s = ((@object.inspect rescue  
@object.to\_s) rescue  
“id ##{@object.object\_id} of class #{@object.class}”)  
msg += " Object is: $!.object == #{s}"  
end  
super msg  
end  
end

# Example:

class MyError \< StandardError  
include AugmentedException  
end

class C  
attr\_reader :x  
def initialize x  
@x = x  
end

def inspect  
#raise # \<-- try this!  
super  
end

def to\_s  
#raise # \<-- try this!  
“C with x=#{@x}”  
end  
end

# If you are rescuing, then you can work with the object itself.

begin  
raise MyError, [“foo bar”, C.new(3)]  
rescue MyError =\> ex  
p ex.message # “foo bar Object is: $!.object == #\<C:0xb7cfb6d0 @x=3\>”  
puts ex # foo bar Object is: $!.object == #\<C:0xb7cfb6d0 @x=3\>  
p ex.object # #\<C:0xb7cfb6d0 @x=3\>  
end

puts

# If all you have is the stack dump, you still get more info

# than before.

raise MyError, [“zap”, C.new(4)]

# c.rb:78: zap Object is: $!.object == #\<C:0xb7da65d0 @x=4\> (MyError)

**END**

/\*  
If you want to generate these exceptions from C code, you will find  
that rb\_raise() doesn’t quite work (it wants a string, not an  
object).  
But the following does work nicely. It’s almost a drop-in replacement  
for rb\_raise (and the implementation is based on MRI). You just add  
an  
argument referencing the object you wish to propagate to the rescuer.  
You can still use format strings as with rb\_raise.  
\*/

#include \<stdarg.h\>

void aug\_ex\_raise(VALUE exc, VALUE obj, const char \*fmt, …)  
{  
va\_list args;  
char buf[BUFSIZ];  
VALUE ex, ary;

```
 va_start(args, fmt);
 vsnprintf(buf, BUFSIZ, fmt, args);
 va_end(args);

 ary = rb_ary_new3(2, rb_str_new2(buf), obj);
 ex = rb_funcall(exc, ID_new, 1, ary);
 rb_exc_raise(ex);

```

}

// Sample use:  
aug\_ex\_raise(exception\_class, object, “foo: %s, %d”, “bar”, 13);

---

<div class="post-metadata">

**Author:** ![Joel\_VanderWerf](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@Joel\_VanderWerf](https://www.ruby-forum.com/u/Joel_VanderWerf)\
**Post date:** [April 6, 2008, 10:55am UTC](https://www.ruby-forum.com/t/augmented-exceptions/133995/2 "2008-04-06T10:55:28Z")

</div>

On Sun, Apr 6, 2008 at 8:47 AM, Joel VanderWerf  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

I guess this will simply save me hours of debugging, thx for sharing.

Robert

–  
[http://ruby-smalltalk.blogspot.com/](http://ruby-smalltalk.blogspot.com/)

* * *

Whereof one cannot speak, thereof one must be silent.  
Ludwig Wittgenstein
