Yes!
October 17, 2009It is my pleasure to announce that after much fooling around with XS, I am now reading temperatures from the device with Perl.
ok 1 - use Vernier::GoTemp; ok 2 - The object isa Vernier::GoTemp # 27.4995159422979 # 27.4995159422979
That is the ambient temperature in Celsius.
Update: My ten-year-old son and I had a grand time measuring temperatures under our armpits, behind our knees, in front of the fan, and in glasses of ice, lukewarm and warm water.
Devel::Peek
October 17, 2009Today I belatedly discovered the usefulness of Devel::Peek.
While hacking on the newly-rebaptised Vernier::GoIO I found that get_device_info was handing me back garbage. Using Devel::Peek helped me pinpoint the problem more quickly.
get_device_info should return an array consisting of device name, vendor ID and product ID; the former is a string in hex and the latter integers required by HID USB.
Devel::Peek to the rescue. At the top of Vernier/GoIO.pm:
use Devel::Peek qw(Dump);
and in the offending routine, a strategically-placed call to Dump:
sub get_device_info {
my $handle = shift;
my @info = Sensor_GetOpenDeviceName($handle);
Dump(\@info);
die "No device info found for $handle!" unless @info;
return (
'name' => $info[0],
'vendor_id' => $info[1],
'product_id' => $info[2],
);
}
Running a test nets me this output:
SV = RV(0x82603c) at 0x86590c
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x8633bc
SV = PVAV(0x864684) at 0x8633bc
REFCNT = 2
FLAGS = (PADBUSY,PADMY)
IV = 0
NV = 0
ARRAY = 0x30b9e0
FILL = 2
MAX = 3
ARYLEN = 0x0
FLAGS = (REAL)
Elt No. 0
SV = PV(0x849ad4) at 0x866074
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x304eb0 "0x1d100000"
CUR = 10
LEN = 12
Elt No. 1
SV = IV(0x80d3ac) at 0x865954
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 8388740
Elt No. 2
SV = IV(0x80d380) at 0x8660c8
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 8388740
An array ref with three elements. Looks good. But those last two values? Not what I had in mind. I should get 2295 and 2, respectively.
perlguts gave me a clue:
Despite their suggestions in earlier versions of this document the macros (X)PUSH[iunp] are not suited to XSUBs which return multiple results. For that, either stick to the (X)PUSHs macros shown above, or use the new m(X)PUSH[iunp] macros instead; see “Putting a C value on Perl stack”.
I’d pushed three scalars on the stack, the first using the macro XPUSHs and the next two with XPUSHi:
XPUSHs(sv_2mortal(newSVpv(name, strlen(name))));
XPUSHi(sv_2mortal(newSViv(vendorId)));
XPUSHi(sv_2mortal(newSViv(productId)));
I changed the XPUSHi to XPUSHs as advised by perlguts, re(generated|compiled|tested) and lo! correct return values.
SV = RV(0x82603c) at 0x86590c
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x8633bc
SV = PVAV(0x864684) at 0x8633bc
REFCNT = 2
FLAGS = (PADBUSY,PADMY)
IV = 0
NV = 0
ARRAY = 0x30b9e0
FILL = 2
MAX = 3
ARYLEN = 0x0
FLAGS = (REAL)
Elt No. 0
SV = PV(0x849ad4) at 0x866074
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x304eb0 "0x1d100000"
CUR = 10
LEN = 12
Elt No. 1
SV = IV(0x80d334) at 0x865954
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 2295
Elt No. 2
SV = IV(0x80d3ac) at 0x8660c8
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 2
perlguts has this to say about why those two integer values were identical:
The following code will not do what you think:
XPUSHi(10);
XPUSHi(20);This translates as “set TARG to 10, push a pointer to TARG onto the stack; set TARG to 20, push a pointer to TARG onto the stack”. At the end of the operation, the stack does not contain the values 10 and 20, but actually contains two pointers to TARG, which we have set to 20.
Okay then. Point taken, and mental note made to study
“Putting a C Value on the Perl Stack” more closely.
Typemapping
October 11, 2009Today’s XS problem was solved with a tweak of the typemap.
I wrote a constructor for the Go! Temp device class Vernier::GoTemp that, amongst other things, ‘opens’ the device; that is, establishes communications with and initializes it.
The C prototype is
GOIO_DLL_INTERFACE_DECL GOIO_SENSOR_HANDLE GoIO_Sensor_Open(
const char *pDeviceName,
gtype_int32 vendorId,
gtype_int32 productId,
gtype_int32 strictDDSValidationFlag);
and the XSUB is
GOIO_SENSOR_HANDLE
GoIO_Sensor_Open(pDeviceName, vendorId, productId, strictDDSValidationFlag)
const char * pDeviceName
gtype_int32 vendorId
gtype_int32 productId
gtype_int32 strictDDSValidationFlag
The make test error I got was
pDeviceName is not of type const charPtr at /Users/zrusilla/proj/goio/Vernier/blib/lib/Vernier/GoIO.pm line 150.
The device name is a hexadecimal number 0x1d100000, so I thought the integer part of the scalar was being passed long to the XSUB, which howled in protest. How to fix? With a tweak of the typemap.
The previous entry for a const char *was
const char * T_PTROBJ
The typemap documentation tells me:
T_PTROBJ
Similar to T_PTRREF except that the reference is blessed into a class. This allows the pointer to be used as an object. Most commonly used to deal with C structs. The typemap checks that the perl object passed into the XS routine is of the correct class (or part of a subclass).The pointer is blessed into a class that is derived from the name of type of the pointer but with all ‘*’ in the name replaced with ‘Ptr’.
Ah, so that’s the problem. When the C header says char *, it means char *. So I changed the typemap entry to a T_PV, which is a plain old char *:
const char * T_PV
rebuilt and tested. And all tests pass.
Effective Perl Programming
October 10, 2009I was excited to learn that Josh McAdams and brian d foy are updating Effective Perl Programming by Joseph Hall and Randal Schwartz.
I have a special fondness for this book, since it helped me understand and write Perl as Perl. I came to Perl from C and shell, and that’s how I wrote it. Not until I read this book did the realization dawn that I had left C’s primitives behind. The deck of cards I was now holding consisted of scalars, arrays and hashes and that I could manipulate these like a Vegas card dealer. It changed everything.
For that reason, and because it’s compact and to the point, it’s a better book to press into the hands of experienced programmers new to Perl than the Camel book. You can carry it easily, read sections on the bus, and get up to speed quickly. The sections on the distinction between map, grep and for, on regular expressions and how to start a module were worth the price alone.
A lot has changed since the book was first published, so an overhaul will give the book new life. I hope Josh and bdf retain its quality as an exceptionally clear, concise guide to Perl for the experienced hacker.
Another day, another XSUB
October 7, 2009Back to the GoIO project.
I’ve got a C subroutine
gtype_int32 GoIO_GetNthAvailableDeviceName(
char *pBuf, //[out] ptr to buffer to store device name string.
gtype_int32 bufSize, //[in] number of bytes in pBuf
gtype_int32 vendorId, //[in] USB vendor id
gtype_int32 productId, //[in] USB product id
gtype_int32 N); //[in] index into list of known devices
// 0 => first device in list.
that I wish to call from Perl as:
my $name = GetNthAvailableDeviceName(vendorID, productID);
dispensing with the need to pass in a string buffer. The XSUB to do this is:
NO_OUTPUT gtype_int32
GoIO_GetNthAvailableDeviceName(gtype_int32 vendorId, gtype_int32 productId)
PREINIT:
char s[GOIO_MAX_SIZE_DEVICE_NAME];
gtype_int32 found;
PPCODE:
found = GoIO_GetNthAvailableDeviceName(s, GOIO_MAX_SIZE_DEVICE_NAME, vendorId, productId, 0);
printf("%d\n", found);
printf("%s\n", s);
if (found == 0) {
EXTEND(SP, 1);
PUSHs(sv_2mortal(newSVpv(s, GOIO_MAX_SIZE_DEVICE_NAME)));
}
The test passes, returning the device name in glorious hexadecimal:
t/05_available_device….# 0x1d100000
Exciting stuff, huh?
I think I’ll go watch some grass grow now.
Pulp Perl
October 6, 2009

Enough Rope to Shoot Yourself in the Foot
No XS revelations tonight. Just a new ‘toon.
The case of the missing constants
October 5, 2009h2xs minus the -A argument should generate code to turn C #define macros and enums into constants accessible by Perl. Yet I could not access the enum from Perl.
The file const-c generated by ExtUtils::Constant contains C routines to determine which value to return for a given string.
case 'N':
if (memEQ(name, "VERNIER_DEFAULT_VENDOR_ID", 25)) {
*iv_return = VERNIER_DEFAULT_VENDOR_ID;
return PERL_constant_ISIV;
}
It’s looking for a #define VERNIER_DEFAULT_VENDOR_ID some place. But there was none to be found. Why?
After much messing around with h2xs I turned to Google and found I was not alone. Alexander Kolbasov wrote an excellent blog post on his difficulties with generating constants from enums.
I decided to cut out the ExtUtils::Constant middleman altogether and define the constants in GoIO.xs, as he suggests. I removed the line
INCLUDE const-xs.inc
and added a new section, taking the opportunity to give the constants friendly names:
PROTOTYPES: ENABLE
#
# Define any constants that need to be exported. By doing it this way we can
# avoid the overhead of using the DynaLoader package, and in addition constants
# defined using this mechanism are eligible for inlining by the perl
# interpreter at compile time.
#
BOOT:
{
HV *stash;
stash = gv_stashpv("GoIO", TRUE);
newCONSTSUB(stash, "VernierID", newSViv(VERNIER_DEFAULT_VENDOR_ID));
newCONSTSUB(stash, "LabPro", newSViv(LABPRO_DEFAULT_PRODUCT_ID));
newCONSTSUB(stash, "GoTemp", newSViv(USB_DIRECT_TEMP_DEFAULT_PRODUCT_ID));
newCONSTSUB(stash, "GoLink", newSViv(SKIP_DEFAULT_PRODUCT_ID));
newCONSTSUB(stash, "GoMotion", newSViv(CYCLOPS_DEFAULT_PRODUCT_ID));
newCONSTSUB(stash, "LabQuest", newSViv(NGI_DEFAULT_PRODUCT_ID));
newCONSTSUB(stash, "CKSpectrometer", newSViv(LOWCOST_SPEC_DEFAULT_PRODUCT_ID));
newCONSTSUB(stash, "MiniGasChromatograph", newSViv(MINI_GC_DEFAULT_PRODUCT_ID));
}
I also removed the entire ExtUtils::Const section from the Makefile.PL.
In lib/GoIO.pm I trimmed all the autoloading code and made the new constant names exportable.
I added a test, t/03_const.t
use strict;
use warnings;
use Test::More tests => 8;use GoIO;
is (VernierID, 0x08F7);
is (LabPro, 1);
is (GoTemp, 2);
is (GoLink, 3);
is (GoMotion, 4);
is (LabQuest, 5);
is (CKSpectrometer, 6);
is (MiniGasChromatograph, 7);
I regenerated the whole chimichanga, ran make test and lo! I have constants.
On to the next hurdle.
Posted by perlgerl 