Codebase list libffi-platypus-perl / 249ace7
add callback example Graham Ollis authored 1 year, 6 months ago Graham✈️✈️ committed 1 year, 6 months ago
4 changed file(s) with 165 addition(s) and 10 deletion(s). Raw diff Collapse all Expand all
20312031 - [curl\_easy\_setopt](https://curl.se/libcurl/c/curl_easy_setopt.html)
20322032 - [curl\_easy\_perform](https://curl.se/libcurl/c/curl_easy_perform.html)
20332033 - [curl\_easy\_cleanup](https://curl.se/libcurl/c/curl_easy_cleanup.html)
2034 - [CURLOPT\_URL](https://curl.se/libcurl/c/CURLOPT_URL.html)
20342035
20352036 ### Perl Source
20362037
20442045 lib => find_lib_or_die(lib => 'curl'),
20452046 );
20462047
2047 # https://curl.se/libcurl/c/curl_easy_init.html
20482048 my $curl_handle = $ffi->function( 'curl_easy_init' => [] => 'opaque' )
20492049 ->call;
20502050
2051 # https://curl.se/libcurl/c/curl_easy_setopt.html
20522051 $ffi->function( 'curl_easy_setopt' => ['opaque', 'enum' ] => ['string'] )
20532052 ->call($curl_handle, CURLOPT_URL, "https://pl.atypus.org" );
20542053
2055 # https://curl.se/libcurl/c/curl_easy_perform.html
20562054 $ffi->function( 'curl_easy_perform' => ['opaque' ] => 'enum' )
20572055 ->call($curl_handle);
20582056
2059 # https://curl.se/libcurl/c/curl_easy_cleanup.html
20602057 $ffi->function( 'curl_easy_cleanup' => ['opaque' ] )
20612058 ->call($curl_handle);
20622059 ```
20912088 [function method](#function) and call it immediately. This is not as
20922089 performant either when you create or call as using the [attach method](#attach),
20932090 but in some cases the performance penalty may be worth it or unavoidable.
2091
2092 ## Callbacks (with libcurl>
2093
2094 ### C API
2095
2096 - [curl\_easy\_init](https://curl.se/libcurl/c/curl_easy_init.html)
2097 - [curl\_easy\_setopt](https://curl.se/libcurl/c/curl_easy_setopt.html)
2098 - [curl\_easy\_perform](https://curl.se/libcurl/c/curl_easy_perform.html)
2099 - [curl\_easy\_cleanup](https://curl.se/libcurl/c/curl_easy_cleanup.html)
2100 - [CURLOPT\_URL](https://curl.se/libcurl/c/CURLOPT_URL.html)
2101 - [CURLOPT\_WRITEFUNCTION](https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION.html)
2102
2103 ### Perl Source
2104
2105 ```perl
2106 use FFI::Platypus 2.00;
2107 use FFI::CheckLib qw( find_lib_or_die );
2108 use FFI::Platypus::Buffer qw( window );
2109 use constant CURLOPT_URL => 10002;
2110 use constant CURLOPT_WRITEFUNCTION => 20011;
2111
2112 my $ffi = FFI::Platypus->new(
2113 api => 2,
2114 lib => find_lib_or_die(lib => 'curl'),
2115 );
2116
2117 my $curl_handle = $ffi->function( 'curl_easy_init' => [] => 'opaque' )
2118 ->call;
2119
2120 $ffi->function( 'curl_easy_setopt' => [ 'opaque', 'enum' ] => ['string'] )
2121 ->call($curl_handle, CURLOPT_URL, "https://pl.atypus.org" );
2122
2123 my $html;
2124
2125 my $closure = $ffi->closure(sub {
2126 my($ptr, $len, $num, $user) = @_;
2127 window(my $buf, $ptr, $len*$num);
2128 $html .= $buf;
2129 return $len*$num;
2130 });
2131
2132 $ffi->function( 'curl_easy_setopt' => [ 'opaque', 'enum' ] => ['(opaque,size_t,size_t,opaque)->size_t'] => 'enum' )
2133 ->call($curl_handle, CURLOPT_WRITEFUNCTION, $closure);
2134
2135 $ffi->function( 'curl_easy_perform' => [ 'opaque' ] => 'enum' )
2136 ->call($curl_handle);
2137
2138 $ffi->function( 'curl_easy_cleanup' => [ 'opaque' ] )
2139 ->call($curl_handle);
2140
2141 if($html =~ /<title>(.*?)<\/title>/) {
2142 print "$1\n";
2143 }
2144 ```
2145
2146 ### Execute
2147
2148 ```
2149 $ perl curl_callback.pl
2150 pl.atypus.org - Home for the Perl Platypus Project
2151 ```
2152
2153 ### Discussion
2154
2155 This example is similar to the previous one, except instead of letting
2156 [libcurl](https://curl.se) write the content body to `STDOUT`, we give
2157 it a callback to send the data to instead. The [closure method](#closure)
2158 can be used to create a callback function pointer that can be called from
2159 C. The type for the callback is in the form `(arg_type,arg_type,etc)->return_type`
2160 where the argument types are in parentheticals with an arrow between the
2161 argument types and the return type.
2162
2163 Inside the closure or callback we use the [window function](https://metacpan.org/pod/FFI::Platypus::Buffer#window)
2164 from [FFI::Platypus::Buffer](https://metacpan.org/pod/FFI::Platypus::Buffer) again to avoid an _extra_ copy. We still
2165 have to copy the buffer to append it to `$hmtl` but it is at least one
2166 less copy.
20942167
20952168 ## bundle your own code
20962169
22502323 [Dist::Zilla::Plugin::FFI::Build](https://metacpan.org/pod/Dist::Zilla::Plugin::FFI::Build) plugin to make this as painless as possible.
22512324
22522325 One of the nice things about the bundle interface is that it is smart enough to
2253 work with either [App::prove](https://metacpan.org/pod/App::prove) or [ExtUtils::MakeMaker](https://metacpan.org/pod/ExtUtils::MakeMaker). This means, unlike
2326 work with either [App::Prove](https://metacpan.org/pod/App::Prove) or [ExtUtils::MakeMaker](https://metacpan.org/pod/ExtUtils::MakeMaker). This means, unlike
22542327 XS, you do not need to explicitly compile your C code in development mode, that
22552328 will be done for you when you call `$ffi->bundle`
22562329
88 lib => find_lib_or_die(lib => 'curl'),
99 );
1010
11 # https://curl.se/libcurl/c/curl_easy_init.html
1211 my $curl_handle = $ffi->function( 'curl_easy_init' => [] => 'opaque' )
1312 ->call;
1413
15 # https://curl.se/libcurl/c/curl_easy_setopt.html
1614 $ffi->function( 'curl_easy_setopt' => ['opaque', 'enum' ] => ['string'] )
1715 ->call($curl_handle, CURLOPT_URL, "https://pl.atypus.org" );
1816
19 # https://curl.se/libcurl/c/curl_easy_perform.html
2017 $ffi->function( 'curl_easy_perform' => ['opaque' ] => 'enum' )
2118 ->call($curl_handle);
2219
23 # https://curl.se/libcurl/c/curl_easy_cleanup.html
2420 $ffi->function( 'curl_easy_cleanup' => ['opaque' ] )
2521 ->call($curl_handle);
0 use strict;
1 use warnings;
2 use FFI::Platypus 2.00;
3 use FFI::CheckLib qw( find_lib_or_die );
4 use FFI::Platypus::Buffer qw( window );
5 use constant CURLOPT_URL => 10002;
6 use constant CURLOPT_WRITEFUNCTION => 20011;
7
8 my $ffi = FFI::Platypus->new(
9 api => 2,
10 lib => find_lib_or_die(lib => 'curl'),
11 );
12
13 my $curl_handle = $ffi->function( 'curl_easy_init' => [] => 'opaque' )
14 ->call;
15
16 $ffi->function( 'curl_easy_setopt' => [ 'opaque', 'enum' ] => ['string'] )
17 ->call($curl_handle, CURLOPT_URL, "https://pl.atypus.org" );
18
19 my $html;
20
21 my $closure = $ffi->closure(sub {
22 my($ptr, $len, $num, $user) = @_;
23 window(my $buf, $ptr, $len*$num);
24 $html .= $buf;
25 return $len*$num;
26 });
27
28 $ffi->function( 'curl_easy_setopt' => [ 'opaque', 'enum' ] => ['(opaque,size_t,size_t,opaque)->size_t'] => 'enum' )
29 ->call($curl_handle, CURLOPT_WRITEFUNCTION, $closure);
30
31 $ffi->function( 'curl_easy_perform' => [ 'opaque' ] => 'enum' )
32 ->call($curl_handle);
33
34 $ffi->function( 'curl_easy_cleanup' => [ 'opaque' ] )
35 ->call($curl_handle);
36
37 if($html =~ /<title>(.*?)<\/title>/) {
38 print "$1\n";
39 }
19411941
19421942 =item L<curl_easy_cleanup|https://curl.se/libcurl/c/curl_easy_cleanup.html>
19431943
1944 =item L<CURLOPT_URL|https://curl.se/libcurl/c/CURLOPT_URL.html>
1945
19441946 =back
19451947
19461948 =head3 Perl Source
19751977 L<function method|/function> and call it immediately. This is not as
19761978 performant either when you create or call as using the L<attach method|/attach>,
19771979 but in some cases the performance penalty may be worth it or unavoidable.
1980
1981 =head2 Callbacks (with libcurl>
1982
1983 =head3 C API
1984
1985 =over 4
1986
1987 =item L<curl_easy_init|https://curl.se/libcurl/c/curl_easy_init.html>
1988
1989 =item L<curl_easy_setopt|https://curl.se/libcurl/c/curl_easy_setopt.html>
1990
1991 =item L<curl_easy_perform|https://curl.se/libcurl/c/curl_easy_perform.html>
1992
1993 =item L<curl_easy_cleanup|https://curl.se/libcurl/c/curl_easy_cleanup.html>
1994
1995 =item L<CURLOPT_URL|https://curl.se/libcurl/c/CURLOPT_URL.html>
1996
1997 =item L<CURLOPT_WRITEFUNCTION|https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION.html>
1998
1999 =back
2000
2001 =head3 Perl Source
2002
2003 # EXAMPLE: examples/curl_callback.pl
2004
2005 =head3 Execute
2006
2007 $ perl curl_callback.pl
2008 pl.atypus.org - Home for the Perl Platypus Project
2009
2010 =head3 Discussion
2011
2012 This example is similar to the previous one, except instead of letting
2013 L<libcurl|https://curl.se> write the content body to C<STDOUT>, we give
2014 it a callback to send the data to instead. The L<closure method|/closure>
2015 can be used to create a callback function pointer that can be called from
2016 C. The type for the callback is in the form C<< (arg_type,arg_type,etc)->return_type >>
2017 where the argument types are in parentheticals with an arrow between the
2018 argument types and the return type.
2019
2020 Inside the closure or callback we use the L<window function|FFI::Platypus::Buffer/window>
2021 from L<FFI::Platypus::Buffer> again to avoid an I<extra> copy. We still
2022 have to copy the buffer to append it to C<$hmtl> but it is at least one
2023 less copy.
19782024
19792025 =head2 bundle your own code
19802026
20462092 L<Dist::Zilla::Plugin::FFI::Build> plugin to make this as painless as possible.
20472093
20482094 One of the nice things about the bundle interface is that it is smart enough to
2049 work with either L<App::prove> or L<ExtUtils::MakeMaker>. This means, unlike
2095 work with either L<App::Prove> or L<ExtUtils::MakeMaker>. This means, unlike
20502096 XS, you do not need to explicitly compile your C code in development mode, that
20512097 will be done for you when you call C<< $ffi->bundle >>
20522098