I built a bit on Richard's work.. On Sat, Mar 3, 2018 at 2:12 AM, Richard Hainsworth <rnhainswo...@gmail.com> wrote: > In addition, I have not figured out how to access CStruct variables yet.
They are mostly like normal classes, but unpacking native C bits isn't always straightforward. In this particular case, ToddAndMargo, you've picked a particularly complicated example. > sub getaddrinfo( <args see below>) returns int32 is native(Str) { * }; You can pass in Str (type object) as you've done here, or just omit and it will work too. > So now we have > sub getaddrinfo( Str $node, Str $service, AddrInfo $hints, AddrInfo $res is > rw) returns int32 is native(Str) { * }; I've had trouble with CStruct types and "is rw", so I usually just make those "Pointer is rw" to get the pointer back, then use nativecast() to make it into the CStruct type I want. > struct sockaddr { > unsigned short sa_family; // address family, AF_xxx > char sa_data[14]; // 14 bytes of protocol address > }; This is another complexity. There are actually multiple address families, and each has its own sockaddr struct. > Whence a Perl6 class: > class SockAddr { > has uint16 $.sa_family; > has CArray[int8] $.sa_data; > } This is incorrect because "CArray[int8]" will make a pointer to the buffer, but the C struct really wants to put the 14 bytes directly here. But that is only for INET addresses, for INET6 (which google.com also has), you need even more bytes here. Thankfully, we can ignore most of that since for this example, we're only reading stuff, not actually trying to create the structure. Building from Richard's work, here's my take: #!/usr/bin/env perl6 use v6; use NativeCall; constant \INET_ADDRSTRLEN = 16; constant \INET6_ADDRSTRLEN = 46; enum AddrInfo-Family ( AF_UNSPEC => 0; AF_INET => 2; AF_INET6 => 10; ); enum AddrInfo-Socktype ( SOCK_STREAM => 1; SOCK_DGRAM => 2; SOCK_RAW => 3; SOCK_RDM => 4; SOCK_SEQPACKET => 5; SOCK_DCCP => 6; SOCK_PACKET => 10; ); enum AddrInfo-Flags ( AI_PASSIVE => 0x0001; AI_CANONNAME => 0x0002; AI_NUMERICHOST => 0x0004; AI_V4MAPPED => 0x0008; AI_ALL => 0x0010; AI_ADDRCONFIG => 0x0020; AI_IDN => 0x0040; AI_CANONIDN => 0x0080; AI_IDN_ALLOW_UNASSIGNED => 0x0100; AI_IDN_USE_STD3_ASCII_RULES => 0x0200; AI_NUMERICSERV => 0x0400; ); sub inet_ntop(int32, Pointer, Blob, int32 --> Str) is native() {} class SockAddr is repr('CStruct') { has uint16 $.sa_family; } class SockAddr-in is repr('CStruct') { has int16 $.sin_family; has uint16 $.sin_port; has uint32 $.sin_addr; method address { my $buf = buf8.allocate(INET_ADDRSTRLEN); inet_ntop(AF_INET, Pointer.new(nativecast(Pointer,self)+4), $buf, INET_ADDRSTRLEN) } } class SockAddr-in6 is repr('CStruct') { has uint16 $.sin6_family; has uint16 $.sin6_port; has uint32 $.sin6_flowinfo; has uint64 $.sin6_addr0; has uint64 $.sin6_addr1; has uint32 $.sin6_scope_id; method address { my $buf = buf8.allocate(INET6_ADDRSTRLEN); inet_ntop(AF_INET6, Pointer.new(nativecast(Pointer,self)+8), $buf, INET6_ADDRSTRLEN) } } class Addrinfo is repr('CStruct') { has int32 $.ai_flags; has int32 $.ai_family; has int32 $.ai_socktype; has int32 $.ai_protocol; has uint32 $.ai_addrlen; has SockAddr $.ai_addr is rw; has Str $.ai_cannonname is rw; has Addrinfo $.ai_next is rw; method flags { do for AddrInfo-Flags.enums { .key if $!ai_flags +| .value } } method family { AddrInfo-Family($!ai_family) } method socktype { AddrInfo-Socktype($!ai_socktype) } method address { given $.family { when AF_INET { nativecast(SockAddr-in, $!ai_addr).address } when AF_INET6 { nativecast(SockAddr-in6, $!ai_addr).address } } } } sub getaddrinfo(Str $node, Str $service, Addrinfo $hints, Pointer $res is rw --> int32) is native() {}; sub freeaddrinfo(Pointer) is native() {} sub MAIN() { my Addrinfo $hint .= new(:ai_flags(AI_CANONNAME)); my Pointer $res .= new; my $rv = getaddrinfo("google.com", Str, $hint, $res); say "return val: $rv"; if ( ! $rv ) { my $addr = nativecast(Addrinfo, $res); while $addr { with $addr { say "Name: ", $_ with .ai_cannonname; say .family, ' ', .socktype; say .address; $addr = .ai_next; } } } freeaddrinfo($res); } I added some enumerated types with the C constants needed, and put in some convenience methods to unpack into them. I used a generic Pointer to get the structure back from C, then nativecast() it to Addrinfo to use it. I put in a while loop to walk through the linked list. Then I check the family, handling only two cases, AF_INET and AF_INET6 (you'll have to add other families if you need them), and cast to the appropriate class for INET vs. INET6. (struct sockaddr_in and struct sockaddr_in6) use a little pointer math to get a pointer to the address of each and pass to inet_ntop() to convert the address to a friendly printable form. Here's what it prints: return val: 0 Name: google.com AF_INET SOCK_STREAM 216.58.219.206 AF_INET SOCK_DGRAM 216.58.219.206 AF_INET SOCK_RAW 216.58.219.206 AF_INET6 SOCK_STREAM 2607:f8b0:4006:800::200e AF_INET6 SOCK_DGRAM 2607:f8b0:4006:800::200e AF_INET6 SOCK_RAW 2607:f8b0:4006:800::200e