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

I've been learning about the Perl API lately, and just playing around a bit. I haven't coded in C in many years, so a lot of it is pretty much guessing.

The below code works as is, and I understand what's happening (per perlapi and perlcall), but I have a few questions.

Before the loop over the file, I define a char of 256 bytes. I'm pretty sure if a line length is longer than that, it'd be a buffer overrun (but I'm not positive).

What I'd like to do, is instead of a char, I'd like to create an empty newSV in its place, but I don't understand the size_t size reference for STRLEN arg in the docs. When I do sizeof(size_t), I get 8 bytes, which is no where near long enough. Can someone please point out my err?

My next issue is that when I do define manually a newSVpvf() I can't figure out to put it onto the stack properly; the perl sub never sees it.

Could anyone in the know please help me understand how to create a new empty newSV() and use it in char* line's place, and beyond that, an example of what I'd need to change once this is done?

use warnings; use strict; use Inline 'C'; c_replace('a.txt'); sub modify_from_c { my $line = shift; $line =~ s/0/5/; return $line; } __END__ __C__ #include <stdio.h> int c_replace(char* fname){ FILE *fh; fh = fopen(fname, "r"); if (! fh){ printf("Couldn't open file %s for reading.\n", fname); return 0; } char* line[256]; while (fgets(line, sizeof(line), fh)){ dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpv(line, sizeof(line)))); PUTBACK; int return_count = call_pv("modify_from_c", G_SCALAR); if (return_count > 1){ croak("invalid return count from modify_from_c()\n"); } SPAGAIN; char* modified_line = POPp; printf("c orig: %sc new: %s", line, modified_line); PUTBACK; FREETMPS; LEAVE; } fclose(fh); }

a.txt simply contains:

10 20 30 40 50

Replies are listed 'Best First'.
Re: Calling perl sub from a C loop using an SV as loop variable (as opposed to char)
by ikegami (Patriarch) on Mar 15, 2016 at 16:25 UTC

    The below code works as is

    Not quite.

    char* line[256]; // An array of 256 pointers, sizeof(line) == 2048
    should be
    char line[256]; // An array of 256 chars, sizeof(line) == 256
    and
    newSVpv(line, sizeof(line)) // length($line) == 2048 (or 256)
    should be
    newSVpv(line, strlen(line)) // length($line) == 3 -or- newSVpv(line, 0) // length($line) == 3

    I'm pretty sure if a line length is longer than that, it'd be a buffer overrun (but I'm not positive).

    No overrun. You'll just get part of the line in one loop, and the rest in subsequent loop passes.

    fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.

    If you want to avoid truncating lines, you can use getline instead of fgets.

    char* line = NULL; ssize_t line_size; while (1) { SV* line_sv; ssize_t len = getline(&line, &line_size, fh); if (len < 0) last; line_sv = newSVpvn(line, len); ... } if (line) free(line);

    Could anyone in the know please help me understand how to create a new empty newSV() and use it in char* line's place

    Why???

    while (1) { // Actually allocates at least 256. SV* line_sv = newSV(255); // We know line_sv has a string buffer, so we can use SvPVX. char* line = SvPVX(line_sv); if (!fgets(line, 256, fh)) { break; } SvCUR_set(line_sv, strlen(line)); SvPOK_on(line_sv); ... // The callback may have changed line_sv, // so line might no longer be valid. line = SvPVbyte_nolen(line_sv); printf("c orig: %sc new: %s", line, modified_line); ... }

    Maybe you want wanted to reuse line_sv instead of creating a new one for each loop pass, but that can be tricky because the callback could have changed it.

      This explains a lot. Thanks ikegami!

      I'll take some time to digest/research some of the things you've pointed out that I wasn't aware of, but I've got a much better understanding already.

      Cheers,

      -stevieb