Re: I need help with NativeCall
I also see another bug: method flags { do for AddrInfo-Flags.enums { .key if $!ai_flags +| .value } } should be method flags { do for AddrInfo-Flags.enums { .key if $!ai_flags +& .value } } Though you probably don't even need the flags.. Curt
Re: I need help with NativeCall
On Sat, Mar 3, 2018 at 10:58 PM, Richard Hainsworth wrote: > I realised from the C documentation that a fixed 14 byte variable was > required, but I could not find a mention about how to map that into Perl6. > Suppose it was necessary to create a structure, how would this be done? There isn't (currently?) a way (that I know of?) to declare an actual array inside a CStruct: (See https://stackoverflow.com/questions/48646159/declaring-an-array-inside-a-perl-6-nativecall-cstruct) I've worked around this a few times. I just stuff enough int64/int32/etc. in there to make the right number of bytes. You can see that in SockAddr-in6, which needs a 16 byte address field. I just put two int64s in there which take 8 bytes each. > Actually you use .allocate below, is this the mechanism? You could do that too. Just make a Buf with the right number of total bytes, but then you have to write the code to access the bits you need within the Buf. CStruct repr gives some nice syntax even if it doesn't (yet) support arrays. >> sub inet_ntop(int32, Pointer, Blob, int32 --> Str) >> is native() {} > > a) Blob is TBD in NativeCall > Some explanation would be useful. A Blob is just a raw bunch of bytes, can be a Blob (immutable) or a Buf (mutable). > b) I used 'returns Str' and you have used '--> Str' > Are the two equivalent and the choice is a programmer style one, or are > there differences? > Is there a reason '-->' is better than 'returns'? Both are identical. I used to use returns all the time. I prefer --> of late since it puts the whole signature between the parens, which I think is nice. I believe the preference is now for -->, and there is talk of deprecating 'returns' eventually. Some discussion here: https://docs.perl6.org/type/Signature#index-entry---%3E-returns-Constraining_Return_Types > I have not come across the stanza 'buf8.allocate(...)' before. https://docs.perl6.org/routine/allocate#role_Blob It just makes a buffer with that number of elements. buf8 is shorthand for Buf[uint8] a buffer of plain 8 bit bytes, so buf8.allocate(n) make a buffer of n bytes. inet_ntop() requires a buffer of already allocated memory to hold the string it is writing. We've declared inet_ntop() as returning a Str, so a pointer to that buffer also gets returned, which Perl then decodes into a new string which gets returned, while the actual buffer falls out of scope and eventually gets garbage collected. > Are these zero initialised automatically by Perl6? I think so (pretty sure), but I'm not certain of this.. It doesn't matter for this, since inet_ntop() doesn't depend on it. It writes a NUL terminated C-String into the buffer. > Also the code 'Pointer.new(nativecast(Pointer,self)+4)' for inet_ntop(), we need a Pointer directly to the address part of the sockaddr structures. self = is Perl 6 reference to the SockAddr-in structure in memory. nativecast(Pointer,self) = turn it into a Pointer that points to the structure in memory. It starts at the beginning of the CStruct, so it points to the '$.sin_family' attribute. Pointers can be treated as integers that are memory addresses. +4 skips over the 2 bytes of sin_family and 2 bytes of sin_port, so it becomes an integer memory address for the sin_addr, but then we need to turn that memory address back into a Pointer with Pointer.new() to use it as a Pointer. nativecast() is a very powerful, but very dangerous way to change the way Perl thinks about a pointer. You can easily get SEGVs if you do it wrong. > I understand you said pointer arithmetic. Where did the +4 for INET and +8 > for INET6 come from? same process for the INET6, but we need to skip over family, port, flowinfo (8 bytes total) to point to the address field. Would be nice to add a "native-cstruct-pointer-to-attribute" or something like that that does what in C would be &(sockaddr->sin_addr) to get the address of an element inside a CStruct. Curt
Re: I need help with NativeCall
I built a bit on Richard's work.. On Sat, Mar 3, 2018 at 2:12 AM, Richard Hainsworth 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( ) 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)
Re: I need help with NativeCall
On 03/04/2018 06:56 AM, Curt Tilmes wrote: I also see another bug: method flags { do for AddrInfo-Flags.enums { .key if $!ai_flags +| .value } } should be method flags { do for AddrInfo-Flags.enums { .key if $!ai_flags +& .value } } Though you probably don't even need the flags.. Curt ¡Ay, caramba!
Re: I need help with NativeCall
Curt, Thanks, your code really helped me. But some questions: 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. This is useful, and a comment in the documentation would be good. 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. I realised from the C documentation that a fixed 14 byte variable was required, but I could not find a mention about how to map that into Perl6. Thankfully, we can ignore most of that since for this example, we're only reading stuff, not actually trying to create the structure. Suppose it was necessary to create a structure, how would this be done? Actually you use .allocate below, is this the mechanism? Since I am not familiar with this C function, I don't know whether there is ever a need to create a SockAddr. sub inet_ntop(int32, Pointer, Blob, int32 --> Str) is native() {} a) Blob is TBD in NativeCall Some explanation would be useful. b) I used 'returns Str' and you have used '--> Str' GTK::Simple uses the 'return' syntax. Are the two equivalent and the choice is a programmer style one, or are there differences? Is there a reason '-->' is better than 'returns'? 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) } } I have not come across the stanza 'buf8.allocate(...)' before. Is this in the missing 'Blob and Buffer' section? Do I understand correctly that it allocates ... 'buf8' units to $buf? Are these zero initialised automatically by Perl6? Also the code 'Pointer.new(nativecast(Pointer,self)+4)' I understand you said pointer arithmetic. Where did the +4 for INET and +8 for INET6 come from? However, nativecast seems to be doing some magic here because 'self' is the class instantiation. Or since everything is a reference in Perl6, everything can be cast to a Pointer? Regards, Richard
Re: I need help with NativeCall
Todd, I figured out some of NativeCall by working with the GTK subroutines. Eventually, I had to write some programs in C to use GTK natively before getting a good understanding about how GTK-Simple works. In addition, I have not figured out how to access CStruct variables yet. So my code does not yet generate much useful information. Anyway, perhaps the following will help you. NativeCall is a bridge between a perl6 program and a compiled library. The first step is to discover the library that holds the function. After a bit of googling, I think getaddrinfo's in 'glibc'. However, it is a standard library and is already loaded, so there is a shortcut. In the first paragraph of NativeCall document we find the line describing how to define a Perl6 function that actually will be the C function: subsome_argless_function() isnative('something') {*} The 'some_argless_function' is the function you want to call, which in your case is 'getaddrinfo', and the 'something' is the name of the library, which for your case is 'glibc'. But for a standard library, all you need is 'Str'. But getaddrinfo has arguments and returns a value. As given by the manual *int getaddrinfo(const char **/node/*, const char **/service/*,* *const struct addrinfo **/hints/*,* *struct addrinfo ***/res/*);* The way you match Perl6 variable types to C types is described in "Passing and Returning Values". Here you really do need some idea about C programming because you are moving information between two languages. So you have to understand something about C to know about the definitions. The return type (in C the type definition in front of the function) is easy because it is an 'int'. From the table 'int' in C is int32 for NativeCall. So we get sub getaddrinfo( ) returns int32 is native(Str) { * }; Note that it is Str without quotes. Now for the arguments. A 'const char *' is a C definition for a string. So this is easy in perl6, we have 'Str' for 'node' and for 'service'. A '*' is a pointer. But here we run into difficulty because hints is defined as 'const struct addrinfo *'. And in the documentation, Pointer[void] is given as the correct NativeCall construct for all pointers. So now we have sub getaddrinfo( Str $node, Str $service, AddrInfo $hints, AddrInfo $res is rw) returns int32 is native(Str) { * }; AddrInfo Type. Since I have not completely solved this, some of the following is wrong or incomplete. The addinfo structure is defined in the manual as struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_tai_addrlen; struct sockaddr *ai_addr; char*ai_canonname; struct addrinfo *ai_next; }; For these, we can easily decode int and char * (strings), but socklen_t needs looking up. The last attribute is a pointer to the next element in the linked lisk. The actual purpose of getattrinfo is to return the first element in the linked list as res. We now need to create our own Type, which in Perl6 means you define a class, which we then can map to the Struct. The NativeCall documentation tells you about this. Following the documentation, I came up with class Addrinfo is repr('CStruct') { has int32 $.ai_flags; has int32 $.ai_family; has int32 $.ai_socktype; has int32 $.ai_protocol; has ??? $.ai_addrlen; has SockAddr $.ai_addr; has Str $.ai_cannonname; has Addrinfo $.ai_next is rw; } Note: 1) ??? is there because I have to find what socklen_t is. 2) the last 'rw' trait. In the section on Pointers, NativeCall simply allows for pointers to types to be mapped using the 'rw' trait. 3) SockAddr is another CStruct to be defined. Socklen_t: I googled and found the following on StackOverflow: https://stackoverflow.com/a/32592809/6293949 It seems that it is an unsigned int at least 32 bits long, but whose size depends on the architecture. So for this piece, I will assume it is a 32. That means socklen_t maps to 'uint32' for NativeCall. SockAddr: Googling led me to another StackOverflow answer: https://stackoverflow.com/a/7415909/6293949 Note that things might not be easy as there may be different structs here. |struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address };| Whence a Perl6 class: class SockAddr { has uint16 $.sa_family; has CArray[int8] $.sa_data; } How to use this? Here is my code (in a file called nativeTest.p6). #!/usr/bin/env perl6 use v6; use NativeCall; class SockAddr is repr('CStruct') { has uint16 $.sa_family; has CArray[int8] $.sa_data; } class Addrinfo is repr('CStruct') { has int32 $.ai_flags; has int32 $.ai_family; has int32 $.ai_socktype; has int
Re: I need help with NativeCall
On 03/02/2018 11:31 AM, ToddAndMargo wrote: Hi All, https://docs.perl6.org/language/nativecall I just don't understand. I want specifically (not a work around) want to make a call to "getaddrinfo" and I don't know how to write in C. (It is part of troubleshooting some other code that uses getaddrinfo. I want to see what it sees.) The port I am look for is TCP 6566 (I can do without the port if need be). The addresses I am looking for is both 192.168.255.10 and 192.168.255.0/24 And I have no clue how to use native call. I know I am being a total mooch, but would some kind person mind showing me how to call "getaddrinfo" with Native Call? Many thanks, -T Forgot to add this: GETADDRINFO(3) Linux Programmer's Manual GETADDRINFO(3) NAME getaddrinfo, freeaddrinfo, gai_strerror - network address and service translation SYNOPSIS #include #include #include int getaddrinfo(const char *node, const char *ser vice, const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo(struct addrinfo *res); const char *gai_strerror(int errcode); Feature Test Macro Requirements for glibc (see feature_test_macros(7)) : -- ~~ Computers are like air conditioners. They malfunction when you open windows ~~
I need help with NativeCall
Hi All, https://docs.perl6.org/language/nativecall I just don't understand. I want specifically (not a work around) want to make a call to "getaddrinfo" and I don't know how to write in C. (It is part of troubleshooting some other code that uses getaddrinfo. I want to see what it sees.) The port I am look for is TCP 6566 (I can do without the port if need be). The addresses I am looking for is both 192.168.255.10 and 192.168.255.0/24 And I have no clue how to use native call. I know I am being a total mooch, but would some kind person mind showing me how to call "getaddrinfo" with Native Call? Many thanks, -T -- ~~~ Serious error. All shortcuts have disappeared. Screen. Mind. Both are blank. ~~~