C::Blocks Advent Day 2

This is the C::Blocks Advent Calendar, in which I release a new treat each day about the C::Blocks library. On the first day I demonstrated how to weave procedural C code into your Perl code. Today I will show how to get data across the boundary between Perl and C.

Unlike every other Perl/C interface library, you do not call your C code as a function. It is merely a block among your Perl code. To see Perl data in your C code, simply name the variable. In this example, I refer to $message directly in my cblock:

use strict;
use warnings;
use C::Blocks;

my $message = 'Merry Christmas!';
cblock {
    char * message = SvPVbyte_nolen($message);
    printf("%s from C::Blocks\n", message);
    sv_setpv($message, "Feliz Navidad!");
}
print "After the cblock, the message is $message\n";

When you run this, you should see:

Merry Christmas! from C::Blocks
After the cblock, the message is Feliz Navidad!

The C::Blocks code extractor identifies when you use Perl variables and correctly injects the appropriate SV*, AV*, or HV* for you. Not only can you get the contents of the Perl data into your C code, but your C code can modify the contents of that variable. For example, it is possible to set up a dualvar:

use strict;
use warnings;
use C::Blocks;

my $message = 'Feliz Navidad!';
cblock {
    /* upgrade to numeric and set to 10 */
    (void)SvUPGRADE($message, SVt_PVIV);
    SvIVX($message) = 10;
    SvIOK_on($message);
}
print "After the cblock, the message is $message\n";
print "adding five, I get ", 5 + $message, "\n";

This should print the string and perform the correct (if goofy) math:

After the cblock, the message is Feliz Navidad!
adding five, I get 15

Every Perl/C interface I know of, including Inline::C, a normal XS module, C::TinyCompiler, XS::TCC, and FFI, bridges from Perl to C via a function call. Oftentimes the C code must be many lines away from the Perl code that uses it, or in a different file entirely. In my own work, I have found myself needing one more piece of information, requiring me to add the variable to the function call and update the function signature before I can even begin using its value in my C code. In some situations where lots of context is needed, it may seem more convenient to add one more argument to an xsub and let feature creep take its toll rather than write a new xsub.

C::Blocks is different: its interface is built around the notion of a code block. In contrast to functions, code blocks live within a larger lexical context with variables that they can use and modify. By placing the block of code directly where it acts, it is possible to resolve references to lexically scoped variables, something that would be incredibly hard to track using other interface schemes. Because it operates with code blocks, it encourages thinking about the side effects of the C code rather than its return values, because that's how blocks of code operate. If you realize you need the value of yet another Perl variable, you can simply refer to it without having to add one more argument to your function, because that's how blocks operate.

Suppose, however, that you want to write an xsub. C::Blocks supports this via the csub keyword. The interface is basic at the moment, requiring you to manipulate Perl's stack directly. Here's an example:

use strict;
use warnings;
use C::Blocks;

csub give_greetings {
    /* setup the stack variables */
    dXSARGS;

    int i;

    /* print each greeting */
    for (i = 0; i < items; ++i)
        printf("%s from csub\n", SvPVbyte_nolen(ST(i)));

    /* Indicate we're not returning anything. */
    XSRETURN(0);
}

give_greetings($_) foreach (
    "Merry Christmas",
    "Froehliche Weihnachten",
    "Joyeux Noel",
    "Buon Natale",
);

When run, this should give:

Merry Christmas from csub
Froehliche Weihnachten from csub
Joyeux Noel from csub
Buon Natale from csub

C::Blocks lets you think of your code in terms of blocks, and this notion includes access to variables in the block's lexical context. Using csub, you can also write Perl-accessible C functions, though these are not (yet) as easy to write as traditional xsubs. Do you always need to "unpack" your Perl scalars using the Perl API, such as sv_setnv? Is it possible to write and reuse C functions? Stay on the lookout for the upcoming Advent Calendar treats to find out.

C::Blocks Advent Day 1 2 3 4 5 6 7 8 9 10 11 12 13

Leave a comment

About David Mertens

user-pic This is my blog about numerical computing with Perl.