add callback example
Graham Ollis authored 1 year, 6 months ago
Graham✈️✈️ committed 1 year, 6 months ago
2031 | 2031 | - [curl\_easy\_setopt](https://curl.se/libcurl/c/curl_easy_setopt.html) |
2032 | 2032 | - [curl\_easy\_perform](https://curl.se/libcurl/c/curl_easy_perform.html) |
2033 | 2033 | - [curl\_easy\_cleanup](https://curl.se/libcurl/c/curl_easy_cleanup.html) |
2034 | - [CURLOPT\_URL](https://curl.se/libcurl/c/CURLOPT_URL.html) | |
2034 | 2035 | |
2035 | 2036 | ### Perl Source |
2036 | 2037 | |
2044 | 2045 | lib => find_lib_or_die(lib => 'curl'), |
2045 | 2046 | ); |
2046 | 2047 | |
2047 | # https://curl.se/libcurl/c/curl_easy_init.html | |
2048 | 2048 | my $curl_handle = $ffi->function( 'curl_easy_init' => [] => 'opaque' ) |
2049 | 2049 | ->call; |
2050 | 2050 | |
2051 | # https://curl.se/libcurl/c/curl_easy_setopt.html | |
2052 | 2051 | $ffi->function( 'curl_easy_setopt' => ['opaque', 'enum' ] => ['string'] ) |
2053 | 2052 | ->call($curl_handle, CURLOPT_URL, "https://pl.atypus.org" ); |
2054 | 2053 | |
2055 | # https://curl.se/libcurl/c/curl_easy_perform.html | |
2056 | 2054 | $ffi->function( 'curl_easy_perform' => ['opaque' ] => 'enum' ) |
2057 | 2055 | ->call($curl_handle); |
2058 | 2056 | |
2059 | # https://curl.se/libcurl/c/curl_easy_cleanup.html | |
2060 | 2057 | $ffi->function( 'curl_easy_cleanup' => ['opaque' ] ) |
2061 | 2058 | ->call($curl_handle); |
2062 | 2059 | ``` |
2091 | 2088 | [function method](#function) and call it immediately. This is not as |
2092 | 2089 | performant either when you create or call as using the [attach method](#attach), |
2093 | 2090 | 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. | |
2094 | 2167 | |
2095 | 2168 | ## bundle your own code |
2096 | 2169 | |
2250 | 2323 | [Dist::Zilla::Plugin::FFI::Build](https://metacpan.org/pod/Dist::Zilla::Plugin::FFI::Build) plugin to make this as painless as possible. |
2251 | 2324 | |
2252 | 2325 | 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 | |
2254 | 2327 | XS, you do not need to explicitly compile your C code in development mode, that |
2255 | 2328 | will be done for you when you call `$ffi->bundle` |
2256 | 2329 |
8 | 8 | lib => find_lib_or_die(lib => 'curl'), |
9 | 9 | ); |
10 | 10 | |
11 | # https://curl.se/libcurl/c/curl_easy_init.html | |
12 | 11 | my $curl_handle = $ffi->function( 'curl_easy_init' => [] => 'opaque' ) |
13 | 12 | ->call; |
14 | 13 | |
15 | # https://curl.se/libcurl/c/curl_easy_setopt.html | |
16 | 14 | $ffi->function( 'curl_easy_setopt' => ['opaque', 'enum' ] => ['string'] ) |
17 | 15 | ->call($curl_handle, CURLOPT_URL, "https://pl.atypus.org" ); |
18 | 16 | |
19 | # https://curl.se/libcurl/c/curl_easy_perform.html | |
20 | 17 | $ffi->function( 'curl_easy_perform' => ['opaque' ] => 'enum' ) |
21 | 18 | ->call($curl_handle); |
22 | 19 | |
23 | # https://curl.se/libcurl/c/curl_easy_cleanup.html | |
24 | 20 | $ffi->function( 'curl_easy_cleanup' => ['opaque' ] ) |
25 | 21 | ->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 | } |
1941 | 1941 | |
1942 | 1942 | =item L<curl_easy_cleanup|https://curl.se/libcurl/c/curl_easy_cleanup.html> |
1943 | 1943 | |
1944 | =item L<CURLOPT_URL|https://curl.se/libcurl/c/CURLOPT_URL.html> | |
1945 | ||
1944 | 1946 | =back |
1945 | 1947 | |
1946 | 1948 | =head3 Perl Source |
1975 | 1977 | L<function method|/function> and call it immediately. This is not as |
1976 | 1978 | performant either when you create or call as using the L<attach method|/attach>, |
1977 | 1979 | 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. | |
1978 | 2024 | |
1979 | 2025 | =head2 bundle your own code |
1980 | 2026 | |
2046 | 2092 | L<Dist::Zilla::Plugin::FFI::Build> plugin to make this as painless as possible. |
2047 | 2093 | |
2048 | 2094 | 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 | |
2050 | 2096 | XS, you do not need to explicitly compile your C code in development mode, that |
2051 | 2097 | will be done for you when you call C<< $ffi->bundle >> |
2052 | 2098 |