File Coverage

blib/lib/FFI/Platypus/Lang/CPP.pm
Criterion Covered Total %
statement 27 27 100.0
branch 5 6 83.3
condition n/a
subroutine 9 9 100.0
pod 2 2 100.0
total 43 44 97.7


line stmt bran cond sub pod time code
1             package FFI::Platypus::Lang::CPP;
2              
3 4     4   34254 use strict;
  4         10  
  4         189  
4 4     4   23 use warnings;
  4         6  
  4         163  
5 4     4   2038 use FFI::ExtractSymbols qw( extract_symbols );
  4         18199  
  4         277  
6 4     4   1943 use FFI::Platypus;
  4         13571  
  4         1386  
7              
8             our $VERSION = '0.04';
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             C++:
18              
19             // on Linux compile with: g++ --shared -o basic.so basic.cpp
20             // elsewhere, consult your C++ compiler documentation
21            
22             class Foo {
23            
24             public:
25            
26             // note you should avoid inlining functions
27             // for classes you intend to use with FFI
28             // as the compiler may not emit code/symbols
29             // for those functions.
30             Foo();
31             ~Foo();
32            
33             int get_bar();
34             void set_bar(int);
35            
36             int _size();
37            
38             private:
39            
40             int bar;
41            
42             };
43            
44             Foo::Foo()
45             {
46             bar = 0;
47             }
48            
49             Foo::~Foo()
50             {
51             }
52            
53             int
54             Foo::get_bar()
55             {
56             return bar;
57             }
58            
59             void
60             Foo::set_bar(int value)
61             {
62             bar = value;
63             }
64            
65             int
66             Foo::_size()
67             {
68             return sizeof(Foo);
69             }
70              
71             Perl:
72              
73             package Foo;
74            
75             use FFI::Platypus;
76             use FFI::Platypus::Memory qw( malloc free );
77            
78             my $ffi = FFI::Platypus->new;
79             $ffi->lang('CPP');
80             $ffi->lib('./basic.so');
81            
82             $ffi->custom_type( Foo => {
83             native_type => 'opaque',
84             perl_to_native => sub { ${ $_[0] } },
85             native_to_perl => sub { bless \$_[0], 'Foo' },
86             });
87            
88             $ffi->attach( [ 'Foo::Foo()' => '_new' ] => ['Foo'] => 'void' );
89             $ffi->attach( [ 'Foo::~Foo()' => '_DESTROY' ] => ['Foo'] => 'void' );
90             $ffi->attach( [ 'Foo::get_bar()' => 'get_bar' ] => ['Foo'] => 'int' );
91             $ffi->attach( [ 'Foo::set_bar(int)'
92             => 'set_bar' ] => ['Foo','int']
93             => 'void' );
94            
95             my $size = $ffi->function('Foo::_size()' => [] => 'int')->call;
96            
97             sub new
98             {
99             my($class) = @_;
100             my $ptr = malloc $size;
101             my $self = bless \$ptr, $class;
102             _new($self);
103             $self;
104             }
105            
106             sub DESTROY
107             {
108             my($self) = @_;
109             _DESTROY($self);
110             free($$self);
111             }
112            
113             package main;
114            
115             my $foo = Foo->new;
116            
117             print $foo->get_bar, "\n"; # 0
118             $foo->set_bar(22);
119             print $foo->get_bar. "\n"; # 22
120              
121             =head1 DESCRIPTION
122              
123             This module provides some hooks for Platypus so that C++ names can be
124             mangled for you. It uses the same primitive types as C. This document
125             also documents issues and caveats that I have discovered in my attempts
126             to work with C++ and FFI.
127              
128             This module is somewhat experimental. It is also available for adoption
129             for anyone either sufficiently knowledgable about C++ or eager enough to
130             learn enough about C++. If you are interested, please send me a pull
131             request or two on the project's GitHub.
132              
133             There are numerous difficulties and caveats involved in using C++
134             libraries from Perl via FFI. This document is intended to enlighten on
135             that subject.
136              
137             Note that in addition to using pre-compiled C++ libraries you can bundle
138             C++ code with your Perl distribution using L. For a
139             complete example, which attempts to address the caveats listed below you
140             can take a look at this sample distro on GitHub:
141              
142             L
143              
144             =head1 CAVEATS
145              
146             In general I have done my research of FFI and C++ using the Gnu C++
147             compiler. I have done some testing with C as well.
148              
149             =head2 name mangling
150              
151             C++ names are "mangled" to handle features such as function overloading
152             and the fact that some characters in the C++ names are illegal machine
153             code symbol names. What this means is that the C++ member function
154             C looks like C<_ZN3Foo7get_barEv> to L.
155             What makes this even trickier is that different C++ compilers provide
156             different mangling formats. When you use the L
157             method to tell Platypus that you are intending to use it with C++, like
158             this:
159              
160             $ffi->lang('CPP');
161              
162             it will mangle the names that you give it. That saves you having to
163             figure out the "real" name for C.
164              
165             The current implementation uses the C command or
166             L if it is installed. If
167             C cannot be found at install time, then
168             L will be made a prerequsite, so
169             you can have some confidence that this feature will work even if your
170             platform does not provide C. The XS module is not a
171             prerequsite when C IS found because using C does not
172             require invoking the compiler and may be more reliable.
173              
174             If the approach to mangling C++ names described above does not work for
175             you, or if it makes you feel slightly queasy, then you can also write C
176             wrapper functions around each C++ method that you want to call from
177             Perl. You can write these wrapper functions right in your C++ code
178             using the C trick:
179              
180             class Foo {
181             public:
182             int bar() { return 1; }
183             }
184            
185             extern "C" int
186             my_bar(Class *foo)
187             {
188             return foo->bar();
189             }
190              
191             Then instead of attaching C attach C.
192              
193             $ffi->attach( my_bar => [ 'Foo' ] => 'int' );
194              
195             =head2 constructors, destructors and methods
196              
197             Constructors and destructors are essentially just functions that do not
198             return a value that need to be called when the object is created and
199             when it is no longer needed (respectively). They take a pointer to the
200             object (C) as their first argument. Constructors can take
201             additional arguments, as you might expect they just come after the
202             object itself. Destructors take no arguments other than the object
203             itself (C).
204              
205             You need to alloate the memory needed for the object before you call the
206             constructor and free it after calling the destructor. The tricky bit is
207             figuring out how much memory to allocate. If you have access to the
208             header file that describes the class and a compiler you can compute the
209             size from within C++ and hand it off to Perl using a static method as I
210             did in the L above.
211              
212             Regular methods also take the object pointer as their first argument.
213             Additional arguments follow, and they may or may not return a value.
214              
215             =head2 inline functions
216              
217             C++ compilers typically do not emit symbols for inlined functions. If
218             you get a message like this:
219              
220             unable to find Foo::get_bar() at basic line 21
221              
222             even though you are sure that class has that method, this is probably
223             the problem that you are having. The Gnu C++ compiler, C has an
224             option to force it to emit the symbols, even for inlined functions:
225              
226             -fkeep-inline-functions # use this
227              
228             Clang has an option to do the opposite of this:
229              
230             -fvisibility-inlines-hidden # do not use this
231              
232             but unhelpfully not a way to keep inlined functions. This appears to be
233             a deliberate design decision made by the clang developers and it makes
234             sense for C++, since inline functions are typically defined in C++
235             header files (.h) so it is difficult to determine in which object file
236             the uninlined inlined functions should go.
237              
238             If you have the source of the C++ and you can recompile it you can also
239             optionally change it to not use inlined functions. In addition to
240             removing any C keywords from the source, you need to move the
241             implementations of any methods outside of the class body. That is, do
242             not do this:
243              
244             class Foo {
245             public:
246             int bar() { return 1; } # WRONG
247             }
248              
249             Do this:
250              
251             class Foo {
252             public:
253             int bar(); # RIGHT
254             }
255            
256             int
257             Foo::bar() # RIGHT
258             {
259             return 1;
260             }
261              
262             =head2 the standard C++ library
263              
264             If you are getting errors like this:
265              
266             unable to find Foo::Foo()
267              
268             that can't be explained by the issues described above, set the
269             environment variable FFI_PLATYPUS_DLERROR to a true value and try again.
270             If you see a warning like this:
271              
272             error loading Foo.so: Foo.so: undefined symbol: __gxx_personality_v0
273              
274             then you probably need to explicitly link with the standard C++ library.
275             The most portable way to deal with this is by using
276             L.
277              
278             =head1 METHODS
279              
280             Generally you will not use this class directly, instead interacting with
281             the L instance. However, the public methods used by
282             Platypus are documented here.
283              
284             =head2 native_type_map
285              
286             my $hashref = FFI::Platypus::Lang::CPP->native_type_map;
287              
288             This returns a hash reference containing the native aliases for the
289             C++ programming languages. That is the keys are native C++ types and the
290             values are libffi native types.
291              
292             =cut
293              
294             sub native_type_map
295             {
296 3     3 1 1664 require FFI::Platypus::Lang::C;
297 3         386 return FFI::Platypus::Lang::C->native_type_map;
298             }
299              
300             =head2 mangler
301              
302             my $mangler = FFI::Platypus::Lang::CPP->mangler($ffi->libs);
303             # prints _ZN9MyInteger7int_sumEii
304             print $mangler->("MyInteger::int_sum(int, int)");
305              
306             Returns a subroutine reference that will "mangle" C++ names.
307              
308             =cut
309              
310             if(eval { require FFI::Platypus::Lang::CPP::Demangle::XS })
311             {
312             *_demangle = \&FFI::Platypus::Lang::CPP::Demangle::XS::demangle;
313             }
314             else
315             {
316 6     6   14892 *_demangle = sub { `c++filt $_[0]` };
317             }
318              
319             sub mangler
320             {
321 1     1 1 1305 my($class, @libs) = @_;
322            
323 1         1 my %mangle;
324            
325 1         2 foreach my $libpath (@libs)
326             {
327             extract_symbols($libpath,
328             export => sub {
329 6     6   3954 my($symbol1, $symbol2) = @_;
330 6         23 my $cpp_symbol = _demangle($symbol2);
331 6 50       47 return unless defined $cpp_symbol;
332 6         14 chomp $cpp_symbol;
333 6 100       54 return if $cpp_symbol eq $symbol2;
334 1         9 $mangle{$cpp_symbol} = $symbol1;
335             },
336 1         6 );
337             }
338            
339             sub {
340 2 100   2   1270 defined $mangle{$_[0]} ? $mangle{$_[0]} : $_[0];
341 1         77 };
342             }
343              
344             1;
345              
346             =head1 EXAMPLES
347              
348             See the above L or the C directory that came with
349             this distribution.
350              
351             =head1 SUPPORT
352              
353             If something does not work as advertised, or the way that you think it
354             should, or if you have a feature request, please open an issue on this
355             project's GitHub issue tracker:
356              
357             L
358              
359             =head1 CONTRIBUTING
360              
361             If you have implemented a new feature or fixed a bug then you may make a
362             pull reequest on this project's GitHub repository:
363              
364             L
365              
366             Caution: if you do this too frequently I may nominate you as the new
367             maintainer. Extreme caution: if you like that sort of thing.
368              
369             This project's GitHub issue tracker listed above is not Write-Only. If
370             you want to contribute then feel free to browse through the existing
371             issues and see if there is something you feel you might be good at and
372             take a whack at the problem. I frequently open issues myself that I
373             hope will be accomplished by someone in the future but do not have time
374             to immediately implement myself.
375              
376             Another good area to help out in is documentation. I try to make sure
377             that there is good document coverage, that is there should be
378             documentation describing all the public features and warnings about
379             common pitfalls, but an outsider's or alternate view point on such
380             things would be welcome; if you see something confusing or lacks
381             sufficient detail I encourage documentation only pull requests to
382             improve things.
383              
384             =head1 SEE ALSO
385              
386             =over 4
387              
388             =item L
389              
390             The Core Platypus documentation.
391              
392             =item L
393              
394             Bundle C or C++ with your FFI / Perl extension.
395              
396             =item L
397              
398             Guess the appropriate C++ compiler / linker flags for your C compiler
399             platform combination.
400              
401             =back
402              
403             =head1 AUTHOR
404              
405             Graham Ollis Eplicease@cpan.orgE
406              
407             =head1 COPYRIGHT AND LICENSE
408              
409             This software is copyright (c) 2015 by Graham Ollis.
410              
411             This is free software; you can redistribute it and/or modify it under
412             the same terms as the Perl 5 programming language system itself.
413              
414             =cut
415