Categories
Perl XS

Playing more with XS: Retrieving subroutine arguments as a hash.

It’s common to use the argument list as a hash in Perl to provide a easier way for the Perl developers interface with subroutines, in this article I will explain how to do the same in XS. Note: not as easy as in Perl.

First we will change the signature like this to take variadic arguments:

void
print(self, ...)
    Peertube_DL_Mega_Helper self
CODE:

As you may now self is the caller object in Perl, not too much trouble, and … is saying basically “Pass me whatever and I will handle it.”, Peertube_DL_Mega_Helper is a custom typemap made by me in the last XS article.

Now we are going to check if users passed us more than a value, for example:

if ( item <= 1 ) {
    croak("Less parameters than expected.");
}

This is a little agresive way to handle this suppose, but this is a demo, feel free to take whatever behaviour you feel more appropiate for your case use.

Now we are going to define a hash to hold our arguments:

HV *hash = newHV();

And some variables useful in the argument processing loop:

bool is_key = true;
char *key;
STRLEN key_len;

And we define the loop to introduce those arguments into a our hash:

for ( int i = 1; i < items; i++ ) {
    if (is_key) {
        key = SvPV(ST(i), key_len);
    } else {    
        SvREFCNT_inc(ST(i));
        if ( !hv_store(hash, key, key_len, ST(i), 0) ) {
            SvREFCNT_dec(ST(i));
        }
    }
    is_key = !is_key;
}

Unfortunatelly this loop misses warning the developer if a even number of elements is passed with default to the last element without value to be undef, we are going to implement also that:

if ( !is_key ) { 
    warn("Even number of parameters in hash argument list.");
    SV *undef = sv_newmortal();
    SvREFCNT_inc(undef);
    if ( !hv_store(hash, key, key_len, sv_newmortal(), 0) ) {
        SvREFCNT_dec(undef);
    }
}

Croak is also an option, but this introduces how to add undefs and I thought would be educative to handle that case as Perl does™

Now we are going to use what we just did to search for the named parameter “hello”, let’s see how:

// Reusing the variable since the name is really convenient and descriptive.
key = "hello"; 
SV **hello = hv_fetch(hash, key, strlen(key), 0);
if ( hello != NULL && *hello != NULL ) {
    if ( !SvOK(*hello) ) {
        warn("hello is undef.");
    }
    printf("hello: %s\n", SvPV_nolen(*hello));
} else {
    croak("Parameter hello required.");
}

After compiling with cpanm . -v to catch errors we are going to run the usual oneliners to check the capabilities we added, I may do an article of testing in Perl later, now I am just learning XS.

sergio@tiki ~/Peertube-dl $ perl -MPeertube::DL::Mega::Helper -e 'Peertube::DL::Mega::Helper->new(3)->print("hello" => "world")';
hello: world
sergio@tiki ~/Peertube-dl $ perl -MPeertube::DL::Mega::Helper -e 'Peertube::DL::Mega::Helper->new(3)->print';
Less parameters than expected. at -e line 1.
sergio@tiki ~/Peertube-dl $ perl -MPeertube::DL::Mega::Helper -e 'Peertube::DL::Mega::Helper->new(3)->print("hello" => "world", "hello" => )';
Even number of parameters in hash argument list. at -e line 1.
hello is undef. at -e line 1.
hello: 

I hope you enjoyed the article.

By sergiotarxz

I am a software developer with high interest on free software.

Leave a Reply

Your email address will not be published. Required fields are marked *