line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package FFI::Platypus::Lang::CPP; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
129350
|
use strict; |
|
4
|
|
|
|
|
20
|
|
|
4
|
|
|
|
|
123
|
|
4
|
4
|
|
|
4
|
|
22
|
use warnings; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
115
|
|
5
|
4
|
|
|
4
|
|
1886
|
use FFI::ExtractSymbols qw( extract_symbols ); |
|
4
|
|
|
|
|
22082
|
|
|
4
|
|
|
|
|
243
|
|
6
|
4
|
|
|
4
|
|
1482
|
use FFI::Platypus 1.00; |
|
4
|
|
|
|
|
16777
|
|
|
4
|
|
|
|
|
1438
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.05'; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
FFI::Platypus::Lang::CPP - Documentation and tools for using Platypus with |
13
|
|
|
|
|
|
|
the C++ programming language |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
B: The original author of this module considered the techniques used by and |
18
|
|
|
|
|
|
|
documented by this module to be somewhate experimental even back in 2015 when he |
19
|
|
|
|
|
|
|
wrote it. The original author now thinks that it is probably safer to write a C API |
20
|
|
|
|
|
|
|
layer between your C++ library and Perl rather than try to call C++ directly as |
21
|
|
|
|
|
|
|
advocated by this module. While the original author has not yet deprecated this |
22
|
|
|
|
|
|
|
module, users of this module should consider its limitations before using it. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
C++: |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
// on Linux compile with: g++ --shared -o basic.so basic.cpp |
27
|
|
|
|
|
|
|
// elsewhere, consult your C++ compiler documentation |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
class Foo { |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
public: |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
// note you should avoid inlining functions |
34
|
|
|
|
|
|
|
// for classes you intend to use with FFI |
35
|
|
|
|
|
|
|
// as the compiler may not emit code/symbols |
36
|
|
|
|
|
|
|
// for those functions. |
37
|
|
|
|
|
|
|
Foo(); |
38
|
|
|
|
|
|
|
~Foo(); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
int get_bar(); |
41
|
|
|
|
|
|
|
void set_bar(int); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
int _size(); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
private: |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
int bar; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
}; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Foo::Foo() |
52
|
|
|
|
|
|
|
{ |
53
|
|
|
|
|
|
|
bar = 0; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Foo::~Foo() |
57
|
|
|
|
|
|
|
{ |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
int |
61
|
|
|
|
|
|
|
Foo::get_bar() |
62
|
|
|
|
|
|
|
{ |
63
|
|
|
|
|
|
|
return bar; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
void |
67
|
|
|
|
|
|
|
Foo::set_bar(int value) |
68
|
|
|
|
|
|
|
{ |
69
|
|
|
|
|
|
|
bar = value; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
int |
73
|
|
|
|
|
|
|
Foo::_size() |
74
|
|
|
|
|
|
|
{ |
75
|
|
|
|
|
|
|
return sizeof(Foo); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Perl: |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
package Foo; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
use FFI::Platypus 1.00; |
83
|
|
|
|
|
|
|
use FFI::Platypus::Memory qw( malloc free ); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my $ffi = FFI::Platypus->new( api => 1 ) |
86
|
|
|
|
|
|
|
$ffi->lang('CPP'); |
87
|
|
|
|
|
|
|
$ffi->lib('./basic.so'); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
$ffi->custom_type( Foo => { |
90
|
|
|
|
|
|
|
native_type => 'opaque', |
91
|
|
|
|
|
|
|
perl_to_native => sub { ${ $_[0] } }, |
92
|
|
|
|
|
|
|
native_to_perl => sub { bless \$_[0], 'Foo' }, |
93
|
|
|
|
|
|
|
}); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo::Foo()' => '_new' ] => ['Foo'] => 'void' ); |
96
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo::~Foo()' => '_DESTROY' ] => ['Foo'] => 'void' ); |
97
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo::get_bar()' => 'get_bar' ] => ['Foo'] => 'int' ); |
98
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo::set_bar(int)' |
99
|
|
|
|
|
|
|
=> 'set_bar' ] => ['Foo','int'] |
100
|
|
|
|
|
|
|
=> 'void' ); |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my $size = $ffi->function('Foo::_size()' => [] => 'int')->call; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub new |
105
|
|
|
|
|
|
|
{ |
106
|
|
|
|
|
|
|
my($class) = @_; |
107
|
|
|
|
|
|
|
my $ptr = malloc $size; |
108
|
|
|
|
|
|
|
my $self = bless \$ptr, $class; |
109
|
|
|
|
|
|
|
_new($self); |
110
|
|
|
|
|
|
|
$self; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub DESTROY |
114
|
|
|
|
|
|
|
{ |
115
|
|
|
|
|
|
|
my($self) = @_; |
116
|
|
|
|
|
|
|
_DESTROY($self); |
117
|
|
|
|
|
|
|
free($$self); |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
package main; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
my $foo = Foo->new; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
print $foo->get_bar, "\n"; # 0 |
125
|
|
|
|
|
|
|
$foo->set_bar(22); |
126
|
|
|
|
|
|
|
print $foo->get_bar. "\n"; # 22 |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head1 DESCRIPTION |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
This module provides some hooks for Platypus so that C++ names can be |
131
|
|
|
|
|
|
|
mangled for you. It uses the same primitive types as C. This document |
132
|
|
|
|
|
|
|
also documents issues and caveats that I have discovered in my attempts |
133
|
|
|
|
|
|
|
to work with C++ and FFI. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
This module is somewhat experimental. It is also available for adoption |
136
|
|
|
|
|
|
|
for anyone either sufficiently knowledgable about C++ or eager enough to |
137
|
|
|
|
|
|
|
learn enough about C++. If you are interested, please send me a pull |
138
|
|
|
|
|
|
|
request or two on the project's GitHub. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
There are numerous difficulties and caveats involved in using C++ |
141
|
|
|
|
|
|
|
libraries from Perl via FFI. This document is intended to enlighten on |
142
|
|
|
|
|
|
|
that subject. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Note that in addition to using pre-compiled C++ libraries you can bundle |
145
|
|
|
|
|
|
|
C++ code with your Perl distribution using L. For a |
146
|
|
|
|
|
|
|
complete example, which attempts to address the caveats listed below you |
147
|
|
|
|
|
|
|
can take a look at this sample distro on GitHub: |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
L |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head1 CAVEATS |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
In general I have done my research of FFI and C++ using the Gnu C++ |
154
|
|
|
|
|
|
|
compiler. I have done some testing with C as well. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=head2 name mangling |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
C++ names are "mangled" to handle features such as function overloading |
159
|
|
|
|
|
|
|
and the fact that some characters in the C++ names are illegal machine |
160
|
|
|
|
|
|
|
code symbol names. What this means is that the C++ member function |
161
|
|
|
|
|
|
|
C looks like C<_ZN3Foo7get_barEv> to L. |
162
|
|
|
|
|
|
|
What makes this even trickier is that different C++ compilers provide |
163
|
|
|
|
|
|
|
different mangling formats. When you use the L |
164
|
|
|
|
|
|
|
method to tell Platypus that you are intending to use it with C++, like |
165
|
|
|
|
|
|
|
this: |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
$ffi->lang('CPP'); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
it will mangle the names that you give it. That saves you having to |
170
|
|
|
|
|
|
|
figure out the "real" name for C. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
The current implementation uses the C command or |
173
|
|
|
|
|
|
|
L if it is installed. If |
174
|
|
|
|
|
|
|
C cannot be found at install time, then |
175
|
|
|
|
|
|
|
L will be made a prerequsite, so |
176
|
|
|
|
|
|
|
you can have some confidence that this feature will work even if your |
177
|
|
|
|
|
|
|
platform does not provide C. The XS module is not a |
178
|
|
|
|
|
|
|
prerequsite when C IS found because using C does not |
179
|
|
|
|
|
|
|
require invoking the compiler and may be more reliable. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
If the approach to mangling C++ names described above does not work for |
182
|
|
|
|
|
|
|
you, or if it makes you feel slightly queasy, then you can also write C |
183
|
|
|
|
|
|
|
wrapper functions around each C++ method that you want to call from |
184
|
|
|
|
|
|
|
Perl. You can write these wrapper functions right in your C++ code |
185
|
|
|
|
|
|
|
using the C trick: |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
class Foo { |
188
|
|
|
|
|
|
|
public: |
189
|
|
|
|
|
|
|
int bar() { return 1; } |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
extern "C" int |
193
|
|
|
|
|
|
|
my_bar(Class *foo) |
194
|
|
|
|
|
|
|
{ |
195
|
|
|
|
|
|
|
return foo->bar(); |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
Then instead of attaching C attach C. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
$ffi->attach( my_bar => [ 'Foo' ] => 'int' ); |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head2 constructors, destructors and methods |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Constructors and destructors are essentially just functions that do not |
205
|
|
|
|
|
|
|
return a value that need to be called when the object is created and |
206
|
|
|
|
|
|
|
when it is no longer needed (respectively). They take a pointer to the |
207
|
|
|
|
|
|
|
object (C) as their first argument. Constructors can take |
208
|
|
|
|
|
|
|
additional arguments, as you might expect they just come after the |
209
|
|
|
|
|
|
|
object itself. Destructors take no arguments other than the object |
210
|
|
|
|
|
|
|
itself (C). |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
You need to alloate the memory needed for the object before you call the |
213
|
|
|
|
|
|
|
constructor and free it after calling the destructor. The tricky bit is |
214
|
|
|
|
|
|
|
figuring out how much memory to allocate. If you have access to the |
215
|
|
|
|
|
|
|
header file that describes the class and a compiler you can compute the |
216
|
|
|
|
|
|
|
size from within C++ and hand it off to Perl using a static method as I |
217
|
|
|
|
|
|
|
did in the L above. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Regular methods also take the object pointer as their first argument. |
220
|
|
|
|
|
|
|
Additional arguments follow, and they may or may not return a value. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head2 inline functions |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
C++ compilers typically do not emit symbols for inlined functions. If |
225
|
|
|
|
|
|
|
you get a message like this: |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
unable to find Foo::get_bar() at basic line 21 |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
even though you are sure that class has that method, this is probably |
230
|
|
|
|
|
|
|
the problem that you are having. The Gnu C++ compiler, C has an |
231
|
|
|
|
|
|
|
option to force it to emit the symbols, even for inlined functions: |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
-fkeep-inline-functions # use this |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Clang has an option to do the opposite of this: |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
-fvisibility-inlines-hidden # do not use this |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
but unhelpfully not a way to keep inlined functions. This appears to be |
240
|
|
|
|
|
|
|
a deliberate design decision made by the clang developers and it makes |
241
|
|
|
|
|
|
|
sense for C++, since inline functions are typically defined in C++ |
242
|
|
|
|
|
|
|
header files (.h) so it is difficult to determine in which object file |
243
|
|
|
|
|
|
|
the uninlined inlined functions should go. |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
If you have the source of the C++ and you can recompile it you can also |
246
|
|
|
|
|
|
|
optionally change it to not use inlined functions. In addition to |
247
|
|
|
|
|
|
|
removing any C keywords from the source, you need to move the |
248
|
|
|
|
|
|
|
implementations of any methods outside of the class body. That is, do |
249
|
|
|
|
|
|
|
not do this: |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
class Foo { |
252
|
|
|
|
|
|
|
public: |
253
|
|
|
|
|
|
|
int bar() { return 1; } # WRONG |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Do this: |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
class Foo { |
259
|
|
|
|
|
|
|
public: |
260
|
|
|
|
|
|
|
int bar(); # RIGHT |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
int |
264
|
|
|
|
|
|
|
Foo::bar() # RIGHT |
265
|
|
|
|
|
|
|
{ |
266
|
|
|
|
|
|
|
return 1; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head2 the standard C++ library |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
If you are getting errors like this: |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
unable to find Foo::Foo() |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
that can't be explained by the issues described above, set the |
276
|
|
|
|
|
|
|
environment variable FFI_PLATYPUS_DLERROR to a true value and try again. |
277
|
|
|
|
|
|
|
If you see a warning like this: |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
error loading Foo.so: Foo.so: undefined symbol: __gxx_personality_v0 |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
then you probably need to explicitly link with the standard C++ library. |
282
|
|
|
|
|
|
|
The most portable way to deal with this is by using |
283
|
|
|
|
|
|
|
L. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head1 METHODS |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
Generally you will not use this class directly, instead interacting with |
288
|
|
|
|
|
|
|
the L instance. However, the public methods used by |
289
|
|
|
|
|
|
|
Platypus are documented here. |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head2 native_type_map |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
my $hashref = FFI::Platypus::Lang::CPP->native_type_map; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
This returns a hash reference containing the native aliases for the |
296
|
|
|
|
|
|
|
C++ programming languages. That is the keys are native C++ types and the |
297
|
|
|
|
|
|
|
values are libffi native types. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=cut |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub native_type_map |
302
|
|
|
|
|
|
|
{ |
303
|
3
|
|
|
3
|
1
|
1165
|
require FFI::Platypus::Lang::C; |
304
|
3
|
|
|
|
|
282
|
return FFI::Platypus::Lang::C->native_type_map; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=head2 mangler |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
my $mangler = FFI::Platypus::Lang::CPP->mangler($ffi->libs); |
310
|
|
|
|
|
|
|
# prints _ZN9MyInteger7int_sumEii |
311
|
|
|
|
|
|
|
print $mangler->("MyInteger::int_sum(int, int)"); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
Returns a subroutine reference that will "mangle" C++ names. |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
=cut |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
if(eval { require FFI::Platypus::Lang::CPP::Demangle::XS }) |
318
|
|
|
|
|
|
|
{ |
319
|
|
|
|
|
|
|
*_demangle = \&FFI::Platypus::Lang::CPP::Demangle::XS::demangle; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
else |
322
|
|
|
|
|
|
|
{ |
323
|
6
|
|
|
6
|
|
21020
|
*_demangle = sub { `c++filt $_[0]` }; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub mangler |
327
|
|
|
|
|
|
|
{ |
328
|
1
|
|
|
1
|
1
|
1359
|
my($class, @libs) = @_; |
329
|
|
|
|
|
|
|
|
330
|
1
|
|
|
|
|
2
|
my %mangle; |
331
|
|
|
|
|
|
|
|
332
|
1
|
|
|
|
|
2
|
foreach my $libpath (@libs) |
333
|
|
|
|
|
|
|
{ |
334
|
|
|
|
|
|
|
extract_symbols($libpath, |
335
|
|
|
|
|
|
|
export => sub { |
336
|
6
|
|
|
6
|
|
5071
|
my($symbol1, $symbol2) = @_; |
337
|
6
|
|
|
|
|
79
|
my $cpp_symbol = _demangle($symbol2); |
338
|
6
|
50
|
|
|
|
110
|
return unless defined $cpp_symbol; |
339
|
6
|
|
|
|
|
19
|
chomp $cpp_symbol; |
340
|
6
|
100
|
|
|
|
123
|
return if $cpp_symbol eq $symbol2; |
341
|
1
|
|
|
|
|
29
|
$mangle{$cpp_symbol} = $symbol1; |
342
|
|
|
|
|
|
|
}, |
343
|
1
|
|
|
|
|
7
|
); |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub { |
347
|
2
|
100
|
|
2
|
|
2400
|
defined $mangle{$_[0]} ? $mangle{$_[0]} : $_[0]; |
348
|
1
|
|
|
|
|
124
|
}; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
1; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=head1 EXAMPLES |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=head2 Using a C++ class without writing bundling any C/C++ code |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
The example in the L shows how you I use a C++ class |
358
|
|
|
|
|
|
|
without writing any wrapper code, though you will have to guess or |
359
|
|
|
|
|
|
|
determine the instance size of the class. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head2 Using a C++ class with a wrapper |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
(For the full source for this example, see examples/wrapper.{pl,cpp} |
364
|
|
|
|
|
|
|
that came with this distribution) |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Sometimes it is easier to write wrapper functions around your new and |
367
|
|
|
|
|
|
|
delete operations. Consider if you add these functions to the C++ |
368
|
|
|
|
|
|
|
source to the example in the L. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
// These could also be class methods |
371
|
|
|
|
|
|
|
extern "C" Foo* |
372
|
|
|
|
|
|
|
Foo_new() |
373
|
|
|
|
|
|
|
{ |
374
|
|
|
|
|
|
|
return new Foo(); |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
extern "C" void |
378
|
|
|
|
|
|
|
Foo_delete(Foo *foo) |
379
|
|
|
|
|
|
|
{ |
380
|
|
|
|
|
|
|
delete foo; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Now we can use this class without having to know I |
384
|
|
|
|
|
|
|
what the size of the class is. We declare the constructor and |
385
|
|
|
|
|
|
|
destructor in Perl space like this: |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo_new' => 'new' ] => [] => 'Foo' ); |
388
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo_delete' => 'DESTROY' ] => ['Foo'] => 'void' ); |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
We've also removed the Perl C and C wrappers as they are |
391
|
|
|
|
|
|
|
unnecessary now, and so the the C++ functions are attached directly to |
392
|
|
|
|
|
|
|
their intended names. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=head2 Exceptions |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
(For the full source of this example, see examples/exception.{pl,cpp} |
397
|
|
|
|
|
|
|
that came with this distribution) |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
If your library throws an exception and you do not catch it in C++ it |
400
|
|
|
|
|
|
|
is going to kill your program. As an example, suppose C in |
401
|
|
|
|
|
|
|
the example above throws an exception: |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
void |
404
|
|
|
|
|
|
|
Foo::set_bar(int value) |
405
|
|
|
|
|
|
|
{ |
406
|
|
|
|
|
|
|
if(value > 512) |
407
|
|
|
|
|
|
|
throw new FooException("too hot"); |
408
|
|
|
|
|
|
|
if(value < 0) |
409
|
|
|
|
|
|
|
throw new FooException("too cold"); |
410
|
|
|
|
|
|
|
bar = value; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
Now if you try to use C with a bad value like this from Perl: |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
$foo->set_bar(-2); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
it will crash your Perl program. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
terminate called after throwing an instance of 'FooException' |
420
|
|
|
|
|
|
|
Abort |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
To handle this, you need to write a wrapper around the C |
423
|
|
|
|
|
|
|
method. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
static FooException *last_exception = NULL; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
extern "C" FooException * |
428
|
|
|
|
|
|
|
Foo_get_exception() |
429
|
|
|
|
|
|
|
{ |
430
|
|
|
|
|
|
|
return last_exception; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
extern "C" void |
434
|
|
|
|
|
|
|
Foo_reset_exception() |
435
|
|
|
|
|
|
|
{ |
436
|
|
|
|
|
|
|
if(last_exception != NULL) |
437
|
|
|
|
|
|
|
delete last_exception; |
438
|
|
|
|
|
|
|
last_exception = NULL; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
extern "C" void |
442
|
|
|
|
|
|
|
Foo_set_bar(Foo *foo, int value) |
443
|
|
|
|
|
|
|
{ |
444
|
|
|
|
|
|
|
try |
445
|
|
|
|
|
|
|
{ |
446
|
|
|
|
|
|
|
Foo_reset_exception(); |
447
|
|
|
|
|
|
|
foo->set_bar(value); |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
catch(FooException *e) |
450
|
|
|
|
|
|
|
{ |
451
|
|
|
|
|
|
|
last_exception = e; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Next we will write an interface to the FooException class in Perl: |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
package FooException; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
use overload '""' => sub { "exception: " . $_[0]->message . "\n" }; |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$ffi->custom_type( FooException => { |
462
|
|
|
|
|
|
|
native_type => 'opaque', |
463
|
|
|
|
|
|
|
perl_to_native => sub { ${ $_[0] } }, |
464
|
|
|
|
|
|
|
native_to_perl => sub { |
465
|
|
|
|
|
|
|
defined $_[0] |
466
|
|
|
|
|
|
|
? (bless \$_[0], 'FooException') |
467
|
|
|
|
|
|
|
: (); |
468
|
|
|
|
|
|
|
}, |
469
|
|
|
|
|
|
|
}); |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
$ffi->attach( |
472
|
|
|
|
|
|
|
[ 'Foo_get_exception' => 'get_exception' ] => [] => 'FooException' |
473
|
|
|
|
|
|
|
); |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
$ffi->attach( |
476
|
|
|
|
|
|
|
[ 'FooException::message()' => 'message' ] => ['FooException'] => 'string' |
477
|
|
|
|
|
|
|
); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
And finally we write a wrapper for the Perl C method. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
$ffi->attach( [ 'Foo_set_bar' => '_set_bar' ] => ['Foo','int'] |
482
|
|
|
|
|
|
|
=> 'void' ); |
483
|
|
|
|
|
|
|
sub set_bar |
484
|
|
|
|
|
|
|
{ |
485
|
|
|
|
|
|
|
my($self, $value) = @_; |
486
|
|
|
|
|
|
|
$self->_set_bar($value); |
487
|
|
|
|
|
|
|
my $error = FooException->get_exception; |
488
|
|
|
|
|
|
|
die $error if $error; |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
And now when we give C a bogus value we get a Perl exception |
492
|
|
|
|
|
|
|
instead of an application crash: |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
exception: too cold |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
So we can easily wrap the call to C in a Perl eval if we want |
497
|
|
|
|
|
|
|
to catch the exception and handle it. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=head1 SUPPORT |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
If something does not work as advertised, or the way that you think it |
502
|
|
|
|
|
|
|
should, or if you have a feature request, please open an issue on this |
503
|
|
|
|
|
|
|
project's GitHub issue tracker: |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
L |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=head1 CONTRIBUTING |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
If you have implemented a new feature or fixed a bug then you may make a |
510
|
|
|
|
|
|
|
pull reequest on this project's GitHub repository: |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
L |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Caution: if you do this too frequently I may nominate you as the new |
515
|
|
|
|
|
|
|
maintainer. Extreme caution: if you like that sort of thing. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
This project's GitHub issue tracker listed above is not Write-Only. If |
518
|
|
|
|
|
|
|
you want to contribute then feel free to browse through the existing |
519
|
|
|
|
|
|
|
issues and see if there is something you feel you might be good at and |
520
|
|
|
|
|
|
|
take a whack at the problem. I frequently open issues myself that I |
521
|
|
|
|
|
|
|
hope will be accomplished by someone in the future but do not have time |
522
|
|
|
|
|
|
|
to immediately implement myself. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
Another good area to help out in is documentation. I try to make sure |
525
|
|
|
|
|
|
|
that there is good document coverage, that is there should be |
526
|
|
|
|
|
|
|
documentation describing all the public features and warnings about |
527
|
|
|
|
|
|
|
common pitfalls, but an outsider's or alternate view point on such |
528
|
|
|
|
|
|
|
things would be welcome; if you see something confusing or lacks |
529
|
|
|
|
|
|
|
sufficient detail I encourage documentation only pull requests to |
530
|
|
|
|
|
|
|
improve things. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=head1 SEE ALSO |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=over 4 |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=item L |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
The Core Platypus documentation. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=item L + L |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
Bundle C or C++ with your FFI / Perl extension. |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=item L |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
Guess the appropriate C++ compiler / linker flags for your C compiler |
547
|
|
|
|
|
|
|
platform combination. |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=back |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head1 AUTHOR |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Graham Ollis Eplicease@cpan.orgE |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
This software is copyright (c) 2015 by Graham Ollis. |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
560
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=cut |
563
|
|
|
|
|
|
|
|