Using magic hash key transformation
Since version 5.10, perl has the ability to magically transform keys on a hash. This feature was added to make fieldhashes possible, but has more uses. Here I'll show you how to do in using Class::Private as an example.
Loading the XS
use 5.010; our $VERSION = '0.05'; XSLoader::load('Class::Private', $VERSION); 1;package Class::Private;
use strict;
use warnings;
use XSLoader;
As you can see, there's not much to the Perl part of this module, it loads some pragma's (the 5.010 is important because this feature will not work on older versions) and then loads the XS.
Casting the magic
SV*
new(class)
SV* class;
CODE:
HV* hash = newHV();
sv_magic((SV*)hash, NULL, PERL_MAGIC_uvar, (const char*)&hash_filter, sizeof hash_filter);
RETVAL = sv_bless(newRV_noinc((SV*)hash), gv_stashsv(class, GV_ADD));
OUTPUT:
RETVAL
The new
is slightly more complicated. It creates a hash, adds the magic to id, and then returns a blessed reference to it. hash_filter
is an argument in the magic casting describes how the transformation should happen. It specifies the function to call on hash access, it's defined like this:
static const struct ufuncs hash_filter = { hash_name_filter, NULL, 0 };
Implementing the magic
hash_name_filter is the real meat of the module. It defines the transformation.
I32 hash_name_filter(pTHX_ IV action, SV* val) {
MAGIC* magic = mg_find(val, PERL_MAGIC_uvar);
if (strstr(SvPV_nolen(magic->mg_obj), "::") == NULL)
magic->mg_obj = sv_2mortal(newSVpvf("%s::%s", CopSTASHPV(PL_curcop), SvPV_nolen(magic->mg_obj)));
return 0;
}
On the first line it retrieves the magic handle. Its member mg_obj is where the key is stored. If you want to modify it, you will have to create a new SV with a different value and assign it to mg_obj. Note that there is no easy way to transmit arguments to the functions (though you could use the IV member in struct ufuncs), I consider this an unfortunate design flaw.
So...what does this actually do?
Looks really interesting if I could understand it ;)
It seems my braindump missed its target. Let's see how I'm going to fix this…