File Coverage

lib/ExtUtils/XSpp/Exception.pm
Criterion Covered Total %
statement 30 32 93.7
branch 1 2 50.0
condition n/a
subroutine 10 11 90.9
pod 8 9 88.8
total 49 54 90.7


line stmt bran cond sub pod time code
1             package ExtUtils::XSpp::Exception;
2 21     21   116 use strict;
  21         42  
  21         746  
3 21     21   118 use warnings;
  21         33  
  21         14783  
4              
5             require ExtUtils::XSpp::Exception::unknown;
6             require ExtUtils::XSpp::Exception::simple;
7             require ExtUtils::XSpp::Exception::stdmessage;
8             require ExtUtils::XSpp::Exception::code;
9             require ExtUtils::XSpp::Exception::perlcode;
10             #require ExtUtils::XSpp::Exception::message;
11             require ExtUtils::XSpp::Exception::object;
12              
13             =head1 NAME
14              
15             ExtUtils::XSpp::Exception - Map C++ exceptions to Perl exceptions
16              
17             =head1 DESCRIPTION
18              
19             This class is both the base class for the different exception handling
20             mechanisms and the container for the global set of exception
21             mappings from C++ exceptions (indicated by a C++ data type to catch)
22             to Perl exceptions. The Perl exceptions are implemented via C.
23              
24             The basic idea is that you can declare the C++ exception types that
25             you want to handle and how you plan to do so by using the C<%exception>
26             directive in your XS++ (or better yet, in the XS++ typemap):
27              
28             // OutOfBoundsException would have been declared
29             // elsewhere as:
30             //
31             // class OutOfBoundsException : public std::exception {
32             // public:
33             // OutOfBoundsException() {}
34             // virtual const char* what() const throw() {
35             // return "You accessed me out of bounds, fool!";
36             // }
37             // }
38            
39             %exception{outOfBounds}{OutOfBoundsException}{stdmessage};
40              
41             If you know a function or method may throw Cs, you
42             can annotate the declaration in your XS++ as follows:
43              
44             double get_from_array(unsigned int index)
45             %catch{outOfBounds};
46              
47             When C now throws an C, the user
48             gets a Perl croak with the message
49             C<"Caught exception of type 'OutOfBoundsException': You accessed me out of bounds, fool!">.
50             There may be any number of C<%catch> directives per method.
51              
52             I Why do we assign another name (C) to the
53             existing C?
54             Because you may need to catch exceptions of the same C++ type with different
55             handlers for different methods. You can, in principle, re-use the C++ exception
56             class name for the exception I name, but that may be confusing to posterity.
57              
58             Instead of adding C<%catch> to methods, you may also specify exceptions that
59             you wish to handle for all methods of a class:
60              
61             class Foo %catch{SomeException,AnotherException} {
62             ...
63             };
64              
65             The C<%catch{Foo,Bar,...}> syntax is shorthand for C<%catch{Foo} %catch{Bar} ...>.
66             If there are exceptions to be caught both from the class and attached
67             to a method directly, the exceptions that are attached to the method only will
68             be handled first. No single type of exceptions will be handled more than once,
69             therefore it is safe to use this precedence to re-order the class-global
70             exception handling for a single method.
71              
72             If there are no C<%catch> decorators on a method, exceptions derived
73             from C will be caught with a generic C
74             handler such as above. Even if there are C<%catch> clauses for the given method,
75             all otherwise uncaught exceptions will be caught with a generic error message
76             for safety.
77              
78             =head1 Exception handlers
79              
80             There are different cases of Perl exceptions that are implemented
81             as sub-classes of C:
82              
83             =over 2
84              
85             =item L
86              
87             implements the most general case of simply throwing a
88             generic error message that includes the name of the
89             C++ exception type.
90              
91             =item L
92              
93             handles C++ exceptions that are derived from C and
94             which provide a C method that will provide an error message.
95             The Perl-level error message will include the C++ exception type name
96             and the exception's C message.
97              
98             =item L
99              
100             allows the user to supply custom C/C++/XS code that will be included in
101             the exception handler verbatim. The code has access to the exception
102             object as the variable C. Your user supplied code
103             is expected to propagate the exception to Perl by calling croak().
104              
105             =cut
106              
107             =begin comment
108              
109             =item L
110              
111             translates C++ exceptions to Perl error messages using a printf-like
112             mask for the message. Potentially filling in place-holders by calling
113             methods on the C++ exception object(!). Not yet implemented.
114             Details to be hammered out.
115              
116             =end comment
117              
118             =item L
119              
120             maps C++ exceptions to throwing an instance of some Perl exception class.
121              
122             Syntax:
123              
124             %exception{myClassyException}{CppException}{object}{PerlClass};
125              
126             Currently, this means just calling Cnew()> and
127             then die()ing with that object in C<$@>. There is no good way to pass
128             information from the C++ exception object to the Perl object.
129             Will change in future.
130              
131             =item L
132              
133             is the default exception handler that is added to the list of handlers
134             automatically during code generation. It simply throws an entirely
135             unspecific error and catches the type C<...> (meaning I).
136              
137             =cut
138              
139             =begin comment
140              
141             =item L
142              
143             allows the user to supply custom Perl code that will be executed
144             in the exception handler. The code currently has no access to the
145             C++ exception object. It is supposed to return a scalar value
146             that is assigned to C<$@>.
147             Highly experimental.
148              
149             =end comment
150              
151             =back
152              
153             There is a special exception handler C which is always
154             available:
155              
156             int foo() %catch{nothing};
157              
158             It indicates that the given method (or function) is to handle no
159             exceptions. It squishes any exception handlers that might otherwise
160             be inherited from the method's class.
161              
162             =head1 METHODS
163              
164             =cut
165              
166             =head2 new
167              
168             Creates a new C.
169              
170             Calls the C<$self-Einit(@_)> method after construction.
171             C must be overridden in subclasses.
172              
173             =cut
174              
175             sub new {
176 190     190 1 324 my $class = shift;
177 190         577 my $this = bless {}, $class;
178              
179 190         921 $this->init( @_ );
180              
181 190         538 return $this;
182             }
183              
184             sub init {
185 190     190 0 270 my $self = shift;
186 190         583 my %args = @_;
187 190         673 $self->{TYPE} = $args{type};
188 190         611 $self->{NAME} = $args{name};
189             }
190              
191             =head2 handler_code
192              
193             Unimplemented in this base class, but must be implemented
194             in all actual exception classes.
195              
196             Generates the C block of code for inclusion
197             in the XS output. First (optional) argument is an integer indicating
198             the number of spaces to use for the first indentation level.
199              
200             =cut
201              
202             sub handler_code {
203 0     0 1 0 Carp::croak("Programmer left 'handler_code' method of his Exception subclass unimplemented");
204             }
205              
206             =head2 indent_code
207              
208             Given a piece of code and a number of spaces to use for
209             global indentation, indents the code and returns it.
210              
211             =cut
212              
213             sub indent_code {
214 187     187 1 254 my $this = shift;
215 187         235 my $code = shift;
216 187         221 my $n = shift;
217 187         322 my $indent = " " x $n;
218 187         4298 $code =~ s/^/$indent/gm;
219 187         865 return $code;
220             }
221              
222             =head2 cpp_type
223              
224             Fetches the C++ type of the exception from the C attribute and returns it.
225              
226             =cut
227              
228             # TODO: Strip pointers and references
229             sub cpp_type {
230 94     94 1 147 my $this = shift;
231 94         366 return $this->type->print;
232             }
233              
234             =head1 ACCESSORS
235              
236             =head2 name
237              
238             Returns the name of the exception.
239             This is the C in C<%exception{myException}{char*}{handler}>.
240              
241             =cut
242              
243 14     14 1 44 sub name { $_[0]->{NAME} }
244              
245             =head2 type
246              
247             Returns the L C++ type that is used for this exception.
248             This is the C in C<%exception{myException}{char*}{handler}>.
249              
250             =cut
251              
252 94     94 1 558 sub type { $_[0]->{TYPE} }
253              
254              
255             =head1 CLASS METHODS
256              
257             =cut
258              
259             my %ExceptionsByName;
260             #my %ExceptionsByType;
261              
262             =head2 add_exception
263              
264             Given an C object,
265             adds this object to the global registry, potentially
266             overwriting an exception map of the same name that was
267             in effect before.
268              
269             =cut
270              
271             sub add_exception {
272 14     14 1 29 my ($class, $exception) = @_;
273              
274 14         59 $ExceptionsByName{$exception->name} = $exception;
275             #push @{$ExceptionsByType{$exception->print} }, $exception;
276 14         46 return();
277             }
278              
279             =head2 get_exception_for_name
280              
281             Given the XS++ name of the exception map, fetches
282             the corresponding C object
283             from the global registry and returns it. Croaks on error.
284              
285             =cut
286              
287             sub get_exception_for_name {
288 17     17 1 27 my ($class, $name) = @_;
289              
290 17 50       47 if (not exists $ExceptionsByName{$name}) {
291 0         0 Carp::confess( "No Exception with the name $name declared" );
292             }
293 17         61 return $ExceptionsByName{$name};
294             }
295              
296              
297             1;