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 |
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; |