TYPEMAP Simple * T_PTROBJ string T_STRING StringMap T_STRING_MAP StringVector T_STRING_VECTOR # Ok, let's step through this one fragment at a time. First we're going to declare the "input" type # (ie: an argument passed in from perl to C++) of T_STRING, which maps to the C++ STL type of std::string. # # SvTYPE is a function that looks at an SV's type to determine if it's an ordinary SV. # It's basically the XS version of "ref". SVt_PV is a string. But you should use SvROK (the other half of "ref") # first, not sure why it's not. If the string typemap receives something that isn't one, it'll warn and return undef. # SvCUR is a function that lets you interrogate the length of a scalar. # SvCUR has a friend named SvLEN that tells you how long it could be. # If it's length is 0, warn and return undef (not always desired, but for now it's fine. # The last line generates a string from the argument given. # "string" is the actual C++ class constructor. # SvPV_nolen lets you extract the value of a scalar and coerce it as a char *, # of unlimited length, because the C++ std::string constructor doesn't care, I think. INPUT T_STRING { if (SvTYPE($arg) != SVt_PV) { warn(\"${Package}::$func_name() -- $var is invalid svtype\"); XSRETURN_UNDEF; } if (SvCUR($arg) == 0) { warn(\"${Package}::$func_name() -- $var is empty\"); XSRETURN_UNDEF; } $var = string(SvPV_nolen($arg)); } # Ok, this next section covers strings returned from C++ functions or methods (hence the OUTPUT), # that need to be transformed back into scalars for perl to deal with. The "sv_setpvn" macro sets # the value of a scalar. OUTPUT T_STRING sv_setpvn($arg, $var.c_str(), $var.size()); # Now things get complicated, let's handle an "array reference" and turn it into a std::vector of std::string's # and pass it into a C++ function or method, yee-hah.. # # AV *av is a perl array. # I32 len is a 32 bit int. (why not just use int?) # SvROK "is this a reference" sort of like ref, except it doesn't tell you which kind. (SvRV dereferences the reference) # SVt_PVAV is Perl's C structure for an Array. # So, the entire line says, "if the argument is a perl array reference:" # Dereference the arg, then downcast it to a pointer of an array type (since we know it is one), assign that to our array type (av). # Assign the length of the arraa, using "av_len". # We return undef if it's length is 0 (not sure why we couldn't return an empty list here, but whatever) # The "else" handles the case when it's not handed an array reference as an argument # # Now for the guts. Iterate over every element (the for loop)..Call the vector's method "push_back". # Create a new string, calling the scalar extraction SvPV_nolen, against the dereferenced value of what av_fetch returned. # It passes the array, the position, and a third argument detailing if Perl needs to auto extend the list # (because you're about to write to that index position). av_fetch returns a pointer to a pointer to an SV, hence the deref'ing "*av_fetch". # lastly, we need to assign the "$var" (what goes to C++ to t_sv, our C++ StringVector, the header shows this is a std::vector INPUT T_STRING_VECTOR { AV *av; I32 len; StringVector t_sv; if(SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVAV){ av = (AV *)SvRV($arg); len = av_len(av) + 1; if(len == 0){ warn(\"${Package}::$func_name() -- $var is empty array reference\"); XSRETURN_UNDEF; } } else { warn(\"${Package}::$func_name() -- $var is not a array reference\"); XSRETURN_UNDEF; } for (I32 i = 0; i < len; i++) { t_sv.push_back(string(SvPV_nolen(*av_fetch(av, i, 0)))); } $var = t_sv; } # Now for String vectors returned back to Perl # If it the C++ vector is empty, it returns undef back to perl # Why is it when I see "sv_2mortal" I think about the intro to the Sega game, "Altered Beast"... "Rise from your grave!" # So many weirdly named keywords in Perl. I love the language, maybe even because of it's quirks. # # Anyway, we create a new Array. (the rest is covered similarly on page 124 of Extending and Embedding Perl). # # SvSetSV will COPY the data from one variable to another, unless they're pointing to the same thing. # newRV_noinc: This is the C/C++ equivalent of the "\" or reference operator. We have a list, we want to return it as an array reference. # We don't increment because it needs to go out of scope when this block finishes (we're copying to "arg" anyway, so the data lives on). # Reading from inside out. Take an array, cast it as a pointer to a scalar (what newRV_noinc requires). Pass that to newRV_noinc to # create a reference. "arg" and this new reference value get sent to SvSetSV to copy the contents of your new array ref to the outbound # "arg" structure. Phew! OUTPUT T_STRING_VECTOR { if($var.empty()){ warn(\"${Package}::$func_name() -- vector is empty\"); XSRETURN_UNDEF; } AV *av = (AV *)sv_2mortal((SV *)newAV()); for(StringVectorIt it = $var.begin(); it != $var.end(); it++) { av_push(av, newSVpvn(it->c_str(), it->size())); } SvSetSV($arg, newRV_noinc((SV *)av)); } # Now we need to look at hash referenecs passed as arguments to C++ functions. # They'll transform into std::map. # HV is a hash value # HE is a hash entry (a key) # Ok, we've seen most of this already. So let's move quick. # If it's not a hash ref, the return undef (SVt_PVHV is a hash) # Next is the C side perl loop over the keys in a hash # The key and value from the hash get forced into scalar values via HeSVKEY_force and HeVAL. # Those scalars are passed into the insert function of our StringMap (std::map) # Assign the ingoing $var to the StringMap we've been inserting into! INPUT T_STRING_MAP { HV *hv; HE *he; StringMap t_sm; if(SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVHV) { hv = (HV *)SvRV($arg); if(hv_iterinit(hv) == 0) { warn(\"${Package}::$func_name() -- $var is empty hash reference\"); XSRETURN_UNDEF; } } else { warn(\"${Package}::$func_name() -- $var is not a hash reference\"); XSRETURN_UNDEF; } while((he = hv_iternext(hv)) != NULL) { SV *svkey = HeSVKEY_force(he); SV *svval = HeVAL(he); t_sm.insert(StringMap::value_type(string(SvPV_nolen(svkey)), string(SvPV_nolen(svval)))); } $var = t_sm; } # Lastly, deal with functions / methods that return StringMap's back to Perl. # If it's empty, return undef.. # That crazy 'rise from the dead' thing again creating a Hash value that will go out of scope and be freed after this block ends. # Now it's basic C++ map looping. hv_store only takes 5 arguments. ::blink:: # The hash that you want insert into # A const char* of the key # An I32 integer of the length of that char* key we just mentioned # A scalar that is to be the value of the key. # The hashing value (passing 0 will tell Perl to compute it for you) # it->first is the key of the current element in std::map # it->second is the value of the current element in std::map # The value needs to be "wrapped" by the creation of a new scalar value, same as what we did when adding elements to an array OUTPUT T_STRING_MAP { if($var.empty()){ warn(\"${Package}::$func_name() -- map is empty\"); XSRETURN_UNDEF; } HV *hv = (HV *)sv_2mortal((SV *)newHV()); for(StringMapIt it = $var.begin(); it != $var.end(); it++) { hv_store(hv, (it->first).c_str(), (it->first).size(), newSVpvn((it->second).c_str(), (it->second).size()), 0); } SvSetSV($arg, newRV_noinc((SV *)hv)); }