Hi there,

I intended to write a little module that would make a Perl variable
read-only (without the help of tie() of course). I thought this would be
easy, but not so obviously! I am using Attribute::Handlers to allow such
a thing:

    my $var : Readonly("I'm immutable");

In Readonly.pm:

    sub Readonly :ATTR(SCALAR) {
        my ($var, $data) = @_[2, 4];
        
        # set value than make it readonly via XS
        $$var = $data;
        _ro_scalar($$var);
    }

In Readonly.xs:

    void
    _ro_scalar (var)
            SV * var;
        PREINIT:
            struct ufuncs uf;
            MAGIC *m;
        CODE:
            uf.uf_val   = &get_val;
            uf.uf_set   = &set_val;
            uf.uf_index = 0;
            sv_magic(var, 0, 'U', (char*)&uf, sizeof(uf)); 
            
//--------> /* valus has been set: now change magic to let svt_set() croak */
            m = mg_find(var, 'U');
            if (m == NULL) 
                printf("no magic\n");
            else /* redefine */
                m->mg_virtual->svt_set = &croak_set_val;

set_val is functionally a no-op:

    I32 set_val (IV i, SV* s) {
        printf("SETTING: %i - %s\n", i, SvPV_nolen(s));
        return 1;
    }

croak_set_val:

    int croak_set_val (SV *s, MAGIC *mg) {
        Perl_croak("\nAttempt to set read-only variable");
        return 1;
    }
    
If the part on from '//--------->' in _ro_scalar() is not there,
set_val() is already called from 'Readonly :ATTR(SCALAR)'. So basically
I need two different svt_set function pointers: one for the initial
setting of the value in Readonly.pm:

    $$var = $data;

and one later (croak_set_val()) when the variable should really be
read-only. Why is this so? In the following code:

    $$var = $data;
    _ro_scalar($$var);

I would assume that the value is first set (which should be no problem
since no magic has yet been attached to $$var) and then any attempt to
re-set the value should invoke set_val. But to me it appears as though
_ro_scalar() is invoked before assigning $data to $$var.

My current workaround is to change mg_virtual->svt_set from &set_val()
to croak_set_val() which works fine. But why do I have to do this?

I also tried it with 'Reload :ATTR(SCALAR, BEGIN)' to let it happen at
compile-time, but to no avail.

Can anyone help?

Tassilo
-- 
$_=q!",}])(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus;})(rekcah{lrePbus;})(lreP{rehtonabus;})(rehtona{tsuJbus!;
$_=reverse;s/sub/(reverse"bus").chr(32)/xge;tr~\n~~d;eval;

Reply via email to