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( <args see below>) 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_t        ai_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 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;
}

sub getaddrinfo( Str $node, Str $service, Addrinfo $hints, Addrinfo $res is rw )
    returns int32
    is native(Str)
    { * };

sub MAIN() {
    my Addrinfo $res .=new;
    my Addrinfo $hint .=new(:ai_flags(0),:ai_family(0),:ai_socktype(0),:ai_protocol(0));
    my $rv = getaddrinfo("google.com", "http", $hint , $res );
    say "return val: $rv";
    if ( ! $rv ) {
        dd $res;
        my SockAddr $sa = $res.ai_addr;
        dd $sa;
        say "ai addr: " ~ $sa.sa_data[0];
        say "ai cannonname: " ~ $res.ai_cannonname;
    }
}

And here is the result:
perl6 nativeTest.p6
return val: 0
Addrinfo $res = Addrinfo.new(ai_flags => -1459219296, ai_family => 22055, ai_socktype => 0, ai_protocol => 0, ai_addrlen => 0, ai_addr => SockAddr, ai_cannonname => Str, ai_next => Addrinfo)
SockAddr $sa = SockAddr
Cannot look up attributes in a SockAddr type object
  in method sa_data at nativeTest.p6 line 5
  in sub MAIN at nativeTest.p6 line 35
  in block <unit> at nativeTest.p6 line 26


As you can see, I have got back data from the NativeCall types, but not from the CStruct types.

The Perl6 NativeCall documentation on CStructs is not clear to me. It talks about containers and dereferencing, so I'm missing something. I would also like to know how to get back information from the CStructs.

Regards,
Richard

On 03/03/2018 04:00 AM, ToddAndMargo wrote:
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 <sys/types.h>
       #include <sys/socket.h>
       #include <netdb.h>

       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))
:





Reply via email to