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

I'd like to give an (XS) routine autovivifying properties, in the sense that it should return a (new, empty) reference fitting the context in which it is called. Thus in
@{ gimme_a_ref() } = ( 1, 2, 3);
it should return an array ref, but called as

$gimme_a_ref->{ haha} = 'hihi';

gimme_a_ref()->{ haha} = 'hihi';
it should return a hash ref, and so on. In non-dereferening calls let it return undef.

I realize that gimme_a_ref() would be useless without further properties, like behaving sticky and returning the same ref after the first call, but that's not the point of my question. I wonder how I could find out in what de-referencing context, if any, I was called.

I notice there are opcodes for the various types of de-referencing, like OP_RV2AV for arrays, etc. I guess I'd have to search the op tree "above me" for one of these, but how would I go about that?

Anno Update: (Unfortunate, because misleading) mistyped example corrected.

Replies are listed 'Best First'.
Re: Autovivifying XS routine
by ikegami (Patriarch) on Jun 01, 2007 at 14:42 UTC

    When you needed an array ref, you used a function. When you needed a hash ref, you used a scalar. If that's all you want, there's no problem. Just have the function always return an array, and have the scalar always contain a hash reference.

    Perhaps what you really what is

    @{ $gimme_a_ref } = ( 1, 2, 3 ); $gimme_a_ref->{haha} = 'hihi';

    or

    @{ gimme_a_ref() } = ( 1, 2, 3 ); gimme_a_ref()->{haha} = 'hihi';

    In other words, perhaps what you really want is to return different references based on the type of reference needed. I don't know if there's a term for that, but it's not auto-vivification.

    In the case of first syntax I presented, operator overloading should do the trick. Check @{} and %{} in overload.

    In the case of second syntax I presented, you could simply return an object with @{} and %{} overloaded (like for the first syntax), or you could mimic WANT. (I hope I have the module name right, I don't have access to CPAN to check.)

      Sounds like you really want
      @{ gimme_a_ref() } = ( 1, 2, 3 );
      You are right, I entirely mistyped the first of the examples. Sorry.

      Thanks for the pointer to Want. I haven't looked at the code, but it seems it does exactly what I want to do (plus a lot more).

      Anno

Re: Autovivifying XS routine
by shmem (Chancellor) on Jun 01, 2007 at 15:30 UTC
    I guess this has to do with Alter :-)

    There's the GIMME_V macro which will tell whether you are in scalar or array context; there's the PL_op macro to get the current OP, which should be 166 for ego itself (method). A COP structure has a member op_next.

    printf("current: %d\n", PL_op->op_type); printf("next: %d\n", PL_op->op_next->op_type); if(GIMME_V) { printf("array context\n"); } else { printf("scalar context\n"); }
    perl -le 'use blib; use Alter qw(ego); $f = ego($a, { 1, 2 });' current: 166 # OP_METHOD next: 6 # OP_GVSV scalar context perl -le 'use blib; use Alter qw(ego); $f = {ego($a, { 1, 2 })}' current: 166 # OP_METHOD next: 144 # OP_ANONLIST huh? array context perl -le 'use blib; use Alter qw(ego); $f = [ego($a, { 1, 2 })]' current: 166 # OP_METHOD next: 143 # OP_LSLICE huh? array context Assertion *strp failed: file "av.c", line 392 at -e line 1. wtf???

    But the next OP could also be something entirely different, depending on position...

    perl -le 'use blib; use Alter qw(ego); $f = {ego($a, { 1, 2 }),1}' current: 166 # OP_METHOD next: 5 # OP_CONST array context perl -le 'use blib; use Alter qw(ego); $f = 1,ego($a, { 1, 2 }),1' current: 166 # OP_METHOD next: 141 # OP_JOIN array context

    Anyways, I hope that helps and gets you on the way :-)

    <update>

    For your examples...

    perl -le 'use blib; use Alter qw(ego); ego($a, { 1, 2 })->{"foo"} = "b +ar"' current: 166 # OP_METHOD next: 134 # OP_EXISTS scalar context Segmentation fault perl -le 'use blib; use Alter qw(ego); @{ ego( $a, { 1, 2 }) } = (1,2, +3)' current: 166 # OP_METHOD next: 178 # OP_ENTER hmm? scalar context Segmentation fault perl -le 'use blib; use Alter qw(ego); ego( $a, { 1, 2 })->[0] = (1,2, +3)' current: 166 # OP_METHOD next: 125 # OP_QUOTEMETA interesting... scalar context Segmentation fault

    I guess the segfaults are quite normal, since ego() isn't type aware yet... but maybe overload() or WANT might suit you better, as ikegami and BrowserUk pointed out, since relying on how perl organizes its opcode chain is - um, well - scary?

    </update>

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      I guess this has to do with Alter :-)

      Good guess :)

      It's not essential, but would be nice to be able to autovivify the Alter ego if called accordingly.

      I'd been looking along similar lines as your code, with about the same amount of huh? and wtf annotations. Now I'll go see how Want does its thing, which includes de-referencing context.

      Anno

        Now I'll go see how Want does its thing, which includes de-referencing context.

        Good idea :-)

        I've looked at Want too, and it has neatly finished what I just started thinking about... wheels... :-P

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Autovivifying XS routine
by BrowserUk (Patriarch) on Jun 01, 2007 at 15:00 UTC

    You can do this fairly easily using overload:

    #! perl -slw use strict; package Gimme; use Scalar::Util qw[ refaddr ]; my %hashes; my %arrays; use overload '%{}' => sub { return $hashes{ refaddr( $_[ 0 ] ) }; }, '@{}' => sub { return $arrays{ refaddr( $_[ 0 ] ) }; }, '""' => sub { return overload::StrVal( $_[ 0 ] ); }, '0+' => sub { return overload::StrVal( $_[ 0 ] ); }, ; sub new { my( $class, $dummy ) = shift; my $self = \$dummy; $hashes{ 0+$self } = {}; $arrays{ 0+$self } = []; return bless $self, $class; } package main; my $gimme = new Gimme; @{ $gimme } = 1 .. 10; print @{ $gimme }; %{ $gimme } = 'A' .. 'Z'; print %{ $gimme }; print $gimme->{ fred } = 'bill'; print $gimme->[ 123 ] = 456; __END__ C:\test>junk9 12345678910 STABOPWXKLYZEFQRMNCDIJGHUV bill 456

    And I've seen overloading done using XS, though I've never done it myself, so in theory at least it should be possible to do it without having to chase opcode trees.


    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.
      Sorry, I mistyped an example (now corrected) that made it look like I wanted the behavior of a variable. That would indeed suggest an overload solution. I want it to be a function, with arguments in the actual case.

      ikegami has pointed out the Want module. That should show me the way.

      Anno

        Okay. I did see the function call, but assumed that the function would return an overloaded blessed reference (as new() in my example does), and then overloading would then come into play to determine the appropraite context.

        It will be interesting to see if Want can detect/supply the calling context more quickly than the overloading. The latter is notoriously slow, but when I played with Want many moons ago, it was also pretty slugish. You might be able to avoid some of that if you c&p the logic directly into your own XS code I suppose?


        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.