Limbic~Region has asked for the wisdom of the Perl Monks concerning the following question:

All,
I rarely try and mix C and perl. When I do, I use Inline::C very sparingly and then use tools to convert to XS for me. I am not a very strong C programmer to begin with so I haven't ever bothered to try and digest the perl API other than whatever that one little bit is to get whatever my current dilema working. Ok, now with that disclaimer of ignorance out of the way, I would like to know how to keep a variable created in C land "alive" when passed back to perl and then back to another C function. Here it is in very broken pseudo code
my $tree = new_tree(); print node_count($tree), "\n"; __END__ __C__ typedef struct { int count; } Tree; SV* new_tree () { Tree* tree; tree.count = 0; /* return tree some how */ } int node_count (SV* tree) { /* get back at tree some how */ return tree.count; }

I have seen the Inline::C-Cookbook example of OO. I don't want an object. If this were pure C, I would pass in a pointer to new_tree and not return anything (glossing over malloc and free). In the CB, tye mentioned "allocate PV space in an SV and stuff it there" which conceptually I understand but have no idea on the implementation. Feel free to mention perlapi, perlxstut, perlguts illustrated, etc though I have all of them on my reading list. I would like a working example to play with as I read though. I know this means I am going to have to handle memory management myself and I am fine with that.

Thanks in advance.

Cheers - L~R

  • Comment on How to keep C variables "alive" between invocations with Inline::C
  • Download Code

Replies are listed 'Best First'.
Re: How to keep C variables "alive" between invocations with Inline::C
by ikegami (Patriarch) on Sep 27, 2010 at 18:54 UTC

    You can set the buffer of a scalar to the C struct, and set SvLEN to zero so Perl doesn't try to free it. Doing almost anything to that scalar will replace the buffer, though, so you might want to have another reference to the C struct somewhere so you can properly free it. But if you're just trying to pass it to your own Perl code, it shouldn't be a problem. See Re: Writing to Pointer Values (PV) in XS.

    If you want Perl to free it, allocate the struct using Perl's memory functions and set SvLEN appropriately.

    In both cases, no destructor is going to be called. That's why I think you might want to use an object on the Perl side. It doesn't look like you want to provide direct access to the internal details of the struct to the Perl code, so I don't see why you'd want to avoid using an object.

    Update: Added second and third paragraphs.

      ikegami,
      If you want Perl to free it, allocate the struct using Perl's memory functions and set SvLEN appropriately.

      I haven't had a chance to read your link yet (still at work) but if it doesn't cover doing that - would you mind providing an example? Ultimately the tree is going to turn into a real tree with branches and leaves so having it all magically get freed if someone does undef $tree would be great.

      It doesn't look like you want to provide direct access to the internal details of the struct to the Perl code, so I don't see why you'd want to avoid using an object.

      I have to beg forgiveness in advance. When I learn new things, I move in a lot of different directions at once. I ignore advice to not do X unless I understand why (usually it means finding out the hard way which I am ok with). My ultimate goal is to be able to write XS naturally. This means improving my mediocre C skills and getting intimately familiar with the perl API. Getting there though, I intend to start by doing some things in Inline::C that are just a little outside my comfort zone and expand from there. Oh, that's the long answer. The short answer is I don't want to have to write code that looks like the cookbook:

      return ((Soldier*)SvIV(SvRV(obj)))->name; vs return soldier.name;

      Cheers - L~R

        Ultimately the tree is going to turn into a real tree with branches and leaves so having it all magically get freed if someone does undef $tree would be great.

        That will only happen if the whole tree is in that one scalar's memory block since no destructor is called.

        The short answer is I don't want to have to write code that looks like the cookbook:

        You should use the typemap to do that for you. I'm pretty sure Inline::C allows you to specify a typemap.

        The short answer is I don't want to have to write code that looks like the cookbook

        If it helps, there's a typemap for the Soldier* type in the InlineX::C2XS Cookbook. (Perhaps it should be added to the Inline::C Cookbook as well.)

        Cheers,
        Rob
Re: How to keep C variables "alive" between invocations with Inline::C
by ig (Vicar) on Sep 27, 2010 at 20:37 UTC

    Maybe something like the following? It is quite simple and avoids the difficulty of translating pointers between C and Perl.

    #!/usr/bin/perl use strict; use warnings; use Inline 'C'; foreach (1..15) { my $tree = new_tree(); die "failed to allocate tree" if($tree < 0); print "node count of tree $tree is: " . node_count($tree), "\n"; } __END__ __C__ #define NMAX 10 struct tree { int size; } trees[NMAX]; int td = 0; int new_tree() { if(td < NMAX) { trees[td].size = td * 2; return(td++); } else { return(-1); } } int node_count(int td) { return(trees[td].size); }

    You could allocate memory dynamically if that were appropriate, perhaps simplifying the trees structure to an array of pointers to allocated memory. With a little more sophistication, you could free tree descriptors, keep track of which ones were in use, etc.

Re: How to keep C variables "alive" between invocations with Inline::C
by ikegami (Patriarch) on Sep 28, 2010 at 00:23 UTC
    { package Node; use strict; use warnings; use Inline C => <<'__EOI__'; typedef struct { // ... } Node; Node* new_node() { Node* node; warn("new_node\n"); // DEBUG Newx(node, 1, Node); // node->... = ...; return node; } void DESTROY(Node* node) { warn("DESTROY\n"); // DEBUG Safefree(node); } __EOI__ } { use strict; use warnings; my $x = Node::new_node(); }

    typemap:

    Node* T_OBJECT INPUT T_OBJECT if (sv_derived_from($arg, \"${subtype}\")) { IV tmp = SvIV((SV*)SvRV($arg)); $var = INT2PTR($type,tmp); } else Perl_croak(aTHX_ \"%s: %s is not of type %s\", ${$ALIAS?\q[GvNAME(CvGV(cv))]:\qq[\"$pname\"]}, \"$var\", \"$subtype\") OUTPUT T_OBJECT sv_setref_pv($arg, \"${subtype}\", (void*)$var);

    I was going to use the T_PTROBJ map for Node*, but it was blessing the objects into NodePtr instead of Node. T_OBJECT is just T_PTROBJ that uses $subtype instead of $type.

      Aren't you missing something? Like where to reference the TYPEMAP


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Inline looks for file "typemap", so there's no need to specify it explicitly.

        $ perl -MInline=FORCE,NOISY,NOCLEAN a.pl ... /home/ikegami/usr/perlbrew/perls/perl-5.12.1/bin/perl /home/ikegami/us +r/perlbrew/perls/perl-5.12.1/lib/5.12.1/ExtUtils/xsubpp -typemap /ho +me/ikegami/usr/perlbrew/perls/perl-5.12.1/lib/5.12.1/ExtUtils/typemap + -typemap /home/ikegami/xxx/typemap Node_ef45.xs > Node_ef45.xsc && + mv Node_ef45.xsc Node_ef45.c ... new_node DESTROY