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

The title basically says it all, but here's some background:

I'm calling some code that expects to receive a hashref. (The XMLout method of XML::Simple, if you must know). I want to pass in a tied hash, since I want to control the order that keys come out when this code runs through them with each. Unfortunately, the code contains this construct:

if (ref($ref) eq 'HASH') { # ... some code ... if (%$ref) { while (($key, $value) = each %$ref) { # ... recurse into the structure ... } } # ... some more code ... }
Now, the problem is that when I pass in a tied reference, it appears that %$ref is always false; that is, the code never even calls each on my tied hash.

Is there some magic method I'm missing to let me have scalar(%h) be true when %h is a tied hash?

-- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/

Replies are listed 'Best First'.
Re: Is it possible to control scalar(%h) when %h is a tied hash?
by thospel (Hermit) on Oct 20, 2004 at 02:49 UTC
    Perl has a new tied method for that since version 5.8.3: "SCALAR". You couldn't do it before that was added. It now also has heuristics to try to give a good scalar value even if you don't have a SCALAR method. On older perls you can try to make a replacement based on if FIRSTKEY returns something or not (but that implies changing the code that uses the hash in scalar context).

    update Older perls returned whatever scalar context would return on the underlying hash that got tied. So if you make sure the hash is not empty before tying, the "if" will always be true, and as explained in a node below, the "while" in your code should then work correctly. So if that kind of use of the hash in scalar context is the only kind happening in your program, that should be a good enough workaround.

      Unfortunately, since I'm calling code in an external module, I can't really change it. Or rather, I can, but subclassing and doing a cut-and-paste of the whole large function is what I was trying to avoid doing by passing in a tied hash.
      -- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/
        You missed what he is saying. If you are using a version greater than 5.8.3, you can add a method, SCALAR, that gets called in exactly the circumstance you were asking about. No changing the module at all. (I haven't tested it, blah blah, just going off what the parent said, etc).
      Ah! The update explains a way around it, and that's what I'll use to fix it, thanks.
      -- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/
Re: Is it possible to control scalar(%h) when %h is a tied hash?
by dragonchild (Archbishop) on Oct 20, 2004 at 02:58 UTC
    If you're pre-5.8, you could overload KEYS using wantarray to determine context. So, you would do if ( keys %$ref) { ... } and it would do the right thing.

    Of course, if your code is exactly like:

    if (%$ref) { while ( my ($k, $v) = each %$ref ) { ... } }
    You could remove the if-check because the while-loop will never execute; it will encounter a false condition on the first check.

    You cannot do this logical refactoring if you have any code before or after the while-loop.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.