Mutual conversion between Perl type and C type in XS
Calling a C function from Perl means converting the Perl type to a C type, calling the C function, returning the result to the C type and returning it to Perl. In other words, most of what you learn is about learning type conversions.
Here, I would like to provide information that can handle any type conversion.
Basic type conversion
First, I will explain the basic type conversion.
From SV * type to IV type
In Perl, the values passed to functions are mostly scalar. The scalar type is the "SV * type" in the world of XS. If a C function requires type IV, then SV * type must be converted to type IV.
SV * type values have integer type values in the area called IV. In other words, in order to convert to type IV, the value of IV contained in SV * type should be taken out.
Use "SvIV (sv)" to retrieve the value of IV owned by a value of type SV *.
In the world of XS, the integer type is mapped to the type IV, so assign it to the IV type. Type IV is defined as large enough to store integers and pointers. In Perl, where 64-bit integers are supported, there is enough size to store 64-bit integers.
# From SV * type to IV type void foo (...) PPCODE: { SV * num_sv = ST (0); IV num = SvIV (num_sv); XSRETURN (0); }
From type IV to type SV *
Next, in order to return it to the Perl world, it is necessary to convert type IV to type SV *. Use newSViv (iv) to create a new SV * type from type IV.
One thing to note is that if you create a new SV * type, be sure to use sv_2mortal (sv) to bring the value to a state called mortal. By setting the value to mortal, memory is automatically freed when SV * is no longer in use. If you forget this, you will get a memory leak.
# From type IV to type SV * void foo (...) PPCODE: { IV num = 3; SV * num_sv = sv_2mortal (newSViv (num)); XPUSHs (num_sv); XSRETURN (1); }
From SV * type to UV type
SV * type values have an unsigned integer value called UV. In other words, in order to convert to UV type, it is sufficient to extract the UV value owned by the SV * type value.
Use "SvUV (sv)" to retrieve the UV value owned by an SV * type value.
In the XS world, unsigned integers are mapped to a type called UV, so substitute them for the UV type. UV and unsigned int are compatible.
# From SV * type to UV type void foo (...) PPCODE: { SV * num_sv = ST (0); UV num = SvUV (num_sv); XSRETURN (0); }
From UV type to SV * type
To return it to the Perl world, you need to convert the UV type to the SV * type. Use newSVuv (uv) to create a new SV * type from a UV type. Don't forget to set the value to mortal.
# From UV type to SV * type void foo (...) PPCODE: { UV num = 5; SV * num_sv = sv_2mortal (newSVuv (num)); XPUSHs (num_sv); XSRETURN (1); }
From SV * type to double type
The double type value is contained in the SV * type NV value. In order to convert to double type, it is enough to take out the value of NV owned by SV * type.
Use "SvNV (sv)" to retrieve the value of NV owned by a value of type SV *.
# From SV * type to double type void foo (...) PPCODE: { SV * num_sv = ST (0); double num = SvNV (num_sv); XSRETURN (0); }
From double type to SV * type
To return it to the Perl world, you need to convert the double type to the SV * type. To create a new SV * type from double type, use newSVnv (nv) . Don't forget to set the value to mortal.
# From double type to SV * type void foo (...) PPCODE: { double num = 3.35; SV * num_sv = sv_2mortal (newSVnv (num)); XPUSHs (num_sv); XSRETURN (1); }
From SV * type to char * type
The char * type value is included in the value PV in the SV * type value. In order to convert to char * type, the value of PV owned by the value of SV * type should be taken out.
Use "SvPV_nolen (sv)" to retrieve the value of PV owned by an SV * type value.
# From SV * type to char * type void foo (...) PPCODE: { SV * str_sv = ST (0); char * str = SvPV_nolen (str_sv); XSRETURN (0); }
From char * type to SV * type
To return it to the Perl world, you need to convert the char * type to the SV * type. To create a new SV * type from a char * type, use newSVpvn (pv, length) .
You need to specify the length of the string in the second argument. I'm using the strlen function to find out the length of the string.
Don't forget to set the value to mortal.
By the way, there is also an API called newSVpv, but this has a function that strlen is called when the length of the string is 0, and it is easy to cause bugs, so it is better to use newSVpvn. It seems to be recommended. (Reference).
# From char * type to SV * type void foo (...) PPCODE: { char * str = "Hello"; SV * str_sv2 = sv_2mortal (newSVpvn (str, strlen (str))); XPUSHs (str_sv2); XSRETURN (1); }
From SV * type to std::string type
# From SV * type to std::string type void foo (...) PPCODE: { SV * str_sv = ST (0); char * str_ch = SvPV_nolen (str_sv); std::string str (str_ch); XSRETURN (0); }
From std::string type to SV * type
# std::string type to SV * type void foo (...) PPCODE: { char * str = "Hello"; const char * str_ch = str.c_str (); SV * str_sv = sv_mortal (newSVpvn (str_ch, strlen (str_ch))); XPUSHs (str_sv); XSRETURN (1); }
Complex type conversion
Now, this time I would like to take up a slightly more complicated type conversion. For example, what if you had a function that took Perl array? You need to convert a Perl array reference to a C array. Let's consider such a complex type conversion this time.
Convert array reference to C language array
First, the array reference is converted to an array (AV * type), then the elements of the array are got one by one and converted to a C language array. The reference is SV * type. You can dereference it with "SvRV (rv)". Dereferencing something that is not a reference will cause a segmentation fault, so be sure to use "SvROK (rv)" to check that the SV * type value is a reference.
void foo (...) PPCODE: { SV * nums_avrv = ST (0); AV * nums_av; if (SvROK (nums_avrv)) { // Dereference and convert to AV * type nums_av = (AV *) SvRV (nums_avrv); } else { croak ("first argument must be array reference"); } // Array length(av_len returns (length -1)) size_t nums_len = av_len (nums_av) + 1; // Memory allocation IV * nums = (IV *) malloc (sizeof (IV) * nums_len); // Create an array for (int i = 0; i <nums_len; i ++) { // Array elements with av_fetch