How to handle hash value type in Perl XS

712 views Asked by At

I need to process hash value depends on value type. Here is code with problem:

I32 keys = hv_iterinit(hash);
for (I32 i = 0; i < keys; i++)
{
  char *key = NULL;
  I32 key_length = 0;
  SV *value = hv_iternextsv(hash, &key, &key_length);
  // SvROK(value);
  if (SvTYPE(SvRV(value)) < SVt_PVAV)
  {
    // handle scalar
    printf("key %s has scalar value\n", key);
  }
  else if (SvTYPE(SvRV(value)) == SVt_PVAV)
  {
    // handle array
    printf("key %s has array value\n", key);
  }
  else if (SvTYPE(SvRV(value)) == SVt_PVHV)
  {
    // handle hash
    printf("key %s has hash value\n", key);
  }
}

If I don't use commented line, I have problem with scalar values. For example with following hash {a => "b", c => {d => "e"}} is produce output:

key c has hash value
key d has scalar value

So here are my questions:

  1. Do we always have reference returned from hv_iternextsv() or sometimes it returns scalar?
  2. Why I don't see scalar value output for key a.

Update.

My mistake was in working with result of hv_iternextsv(). I was thinking that is always a reference. Here is how working code looks like:

I32 keys = hv_iterinit(hash);
for (I32 i = 0; i < keys; i++)
{
  char *key = NULL;
  I32 key_length = 0;
  SV *value = hv_iternextsv(hash, &key, &key_length);
  if (!SvROK(value))
  {
    // handle scalar
  }
  else
  {
    if (SvTYPE(SvRV(value)) == SVt_PVAV)
    {
      // handle array
    }
    else if (SvTYPE(SvRV(value)) == SVt_PVHV)
    {
      // handle hash
    }
  }
}
1

There are 1 answers

4
ikegami On BEST ANSWER

Do we always have reference returned from hv_iternextsv() or sometimes it returns scalar?

It always returns a scalar. Hashes values can only be scalars. Those scalars can be references ($h{x} = [];), but need not be ($h{y} = 123;).

Why I don't see scalar value output for key a.

There's no way your could possibly return what you said it does, seeing as your hash has no key named d. For the hash you provided, your code outputs the following:

key a has scalar value
key c has hash value

But it's more of a coincidence than anything else that you got the right answer. SvTYPE(SvRV(value)) when value isn't a reference??? That makes no sense! The fixed code follows:

use strict;
use warnings;

use Inline C => <<'__EOI__';

  void print_keys(HV* hash) {
    char *key;
    I32 key_length;
    SV *value;

    hv_iterinit(hash);
    while (value = hv_iternextsv(hash, &key, &key_length)) {
      if (SvROK(value)) {
        SV * const referenced = SvRV(value);
        if (SvTYPE(referenced) == SVt_PVAV) {
          printf("The value at key %s is reference to an array\n", key);
        }
        else if (SvTYPE(referenced) == SVt_PVHV) {
           printf("The value at key %s is a reference to a hash\n", key);
        }
        else {
           printf("The value at key %s is a reference\n", key);
        }
      } else {
        printf("The value at key %s is not a reference\n", key);
      }
    }
  }

__EOI__

print_keys({a => "b", c => {d => "e"}});

Output:

The value at key a is not a reference
The value at key c is a reference to a hash