Big Willy has asked for the wisdom of the Perl Monks concerning the following question:

I was unsure if this was the best location for this question, but I saw no better place...

Working with Libnet to make a perl interface ala XS I came across this function prototype:

int libnet_init_packet(size_t p_size, u_char **buf);<br><br>
This function essentially is a wrapper to malloc and passes back the pointer by reference. I assumed this is what i ought to put in my typemap:

u_char ** T_PV
Then in my XS file:

int libnet_init_packet(p_size, buf) size_t p_size u_char & * buf OUTPUT: buf
However, I try to make this and I get an error at "u_char & * buf". Shouldn't this work? If no, how can I manage around this? Simply assume the value I pass from Perl holds a pointer and pass it in as a reference then possibly have something like this in my XS file?

int libnet_init_packet(p_size, buf) size_t p_size void & buf OUTPUT: buf
The obvious typecasting issues aside, this seems like it might work. Your help is sincerely appreciated it. BTW, this is my first XS project. Thanks!

Replies are listed 'Best First'.
(tye)Re: XS question: typemap for a pointer to a pointer?
by tye (Sage) on Apr 04, 2001 at 21:53 UTC

    You need a typemap for "u_char *" (just the one "*") and use "u_char * &buf" as the declaration for the argument.

    This is from fuzzy memory so let me know if that worked.

            - tye (but my friends call me "Tye")
      I made the changes as you suggested. My XS file now reads:
      int libnet_init_packet(p_size, buf) size_t p_size u_char * & buf OUTPUT: buf
      I also did the same thing for libnet_destory_packet(). It now compiles as I hoped it would, but I get a memory fault when I run this test script:
      # Before `make install' is performed this script should be runnable wi +th # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on f +ailure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..1\n"; } END {print "not ok 1\n" unless $loaded;} use Net::LibnetRaw; $loaded = 1; print "ok 1\n"; my $buf; Net::LibnetRaw::libnet_init_packet(IP_H, $buf); my $dip = Net::LibnetRaw::libnet_name_resolve("10.0.0.4", 1); my $sip = Net::LibnetRaw::libnet_name_resolve("10.0.0.1", 1); #my $sock = Net::LibnetRaw::libnet_open_raw_sock(IPPROTO_RAW); Net::LibnetRaw::libnet_build_ip(ICMP_ECHO_H, 0, 12345, 0, 127, 0, $sip +, $dip, undef, 0, $buf); Net::LibnetRaw::libnet_write_ip($sock, $buf, IP_H + ICMP_ECHO_H); Net::LibnetRaw::libnet_close_raw_sock($sock); Net::LibnetRaw::libnet_destroy_packet($buf); ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code):
      It appears that the error occurs when I call libnet_build_ip(). It seems pretty clear that there is an issue with the passage and management of buf. Buf is supposed to come back out by reference here. My XSUB for libnet_build_ip() is:
      int libnet_build_ip(len, tos, id, frag, ttl, prot, saddr, daddr, payload, +payload_s, buf) u_short len u_char tos u_short id u_short frag u_char ttl u_char prot u_long saddr u_long daddr const u_char * payload int payload_s u_char & buf OUTPUT: buf
      It is appropriate for the function prototype. buf is not passed as a C "u_char **" like with libnet_init_packet(). buf should be holding a C pointer to the meory location allocated by libnet_packet_init(), which is basically a wrapper to malloc() which passed the buf back by reference.

      My guess is that there is some issue related to storing a pointer to a memory location used in C in a perl reference. If my guess is correction, how do I work around this? If not, then what the hell am I doing wrong? I've considered writing additional C code to simply allow $buf to hold a number which would act sort of like a "buffer descriptor" ala UNIX file I/O and keep the pointers in perl, but I really want to do as much of this in perl as possible.

      Your solutions are greatly appreciated.

        Can you post the generated C code that relates to the processing of the problem argument (near the top and bottom of each generated subroutine)? You want the pointer to be stashed as an opaque value (usually an unsigned long integer, UV). If you have the typemap class wrong then the C code might be trying to copy the data in the allocated buffer rather than just stashing the pointer's value.

        I don't recall which typemap class is meant to do this and looking at the generated C code prevents me from having to assembly the pieces myself and possible get confused in the process (I'm often easily confused).

                - tye (but my friends call me "Tye")