C::Blocks Advent Day 4

This is the C::Blocks Advent Calendar, in which I release a new treat each day about the C::Blocks library. Yesterday I showed how to get information across the boundary between Perl and C with minimal boiler plate. Today I explain how to declare and use C functions. I also explain the killer feature of C::Blocks: declaring C functions and other things within a module that can be used throughout your code.

I will begin with a specific implementation of a random number generator. This is a decent candidate for a C::Blocks implementation when you anticipate needing to produce many random numbers, such as for Monte Carlo simulations. I came across a nice little writeup about random number generators by David Jones, and the author provides a decent starting point called KISS. How would that library be implemented using C::Blocks? Place the global variables and the function in a clex block, then call the function in your cblocks, like so:

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

# Implement KISS random number generator, copy-and-pasted from
# http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf
clex {
    /* Note: y must never be set to zero;
     * z and c must not be simultaneously zero */
    static unsigned int x = 123456789,y = 362436000,
        z = 521288629,c = 7654321; /* State variables */

    unsigned int KISS() {
        unsigned long long t, a = 698769069ULL;
        x = 69069*x+12345;
        y ^= (y<<13); y ^= (y>>17); y ^= (y<<5); 
        t = a*z+c; c = (t>>32);
        return x+y+(z=t);
    }
}

use C::Blocks::Types qw(uint);

# Get a random number from the generator
my uint $random = 0;
cblock { $random = KISS(); }
print "Random number is $random\n";

When run, this produces

$ perl test.pl 
Random number is 2079675107

As you can see, the clex block contains a function and some global variable declarations which are available to the function and to any cblocks later in the same lexical scope. Indeed, as the name suggests, the visibility of the declarations in clex blocks are lexically scoped. As you know, you cannot use a Perl variable outside of its lexical scope. Likewise, the Perl block that surrounds a clex block defines the scope of the declarations in that block: a cblock or clex block outside of that scope will not be able to see the declarations.

David Jones makes a number of suggestions for improving the generator, but the aim of this Advent treat is not really to implement a random number generator. I simply want to use the idea of such a generator to motivate some of the features of C::Blocks.

A random number generator is meant to be reused. I could of course create a Perl function with the cblock in it, making it reusable from Perl. But how do I make my C functions sharable to other modules and scripts? The answer is simple. Place the clex block in a module with a package name matching the module name, and change clex to cshare. For example, here is a module that provides the same random number generator, called MyRNG.pm:

# MyRNG.pm
package MyRNG;
use strict;
use warnings;
use C::Blocks;
use C::Blocks::Types qw(uint);

# Implement KISS random number generator, copy-and-pasted from
# http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf
cshare {
    unsigned int x, y, z, c;

    unsigned int KISS() {
        unsigned long long t, a = 698769069ULL;
        x = 69069*x+12345;
        y ^= (y<<13); y ^= (y>>17); y ^= (y<<5); 
        t = a*z+c; c = (t>>32);
        return x+y+(z=t);
    }
}

# Set up state
my uint ($x, $y, $z, $c) = map 1 + int(rand(4294967294)), 1 .. 4;
cblock {
    x = $x;
    y = $y;
    z = $z;
    c = $c;
}
1;

Notice that this module initializes the state to random numbers via a cblock. For cblocks written within the module itself, there is no difference between clex and cshare. The difference comes in which blocks have their symbol tables shared when the module is used. Here I illustrate that I can use a local cblock to initialize global variables declared in a cshare block.

An example of a script that uses this module:

use strict;
use warnings;
use C::Blocks;
use C::Blocks::Types qw(uint);

use MyRNG; # import KISS()

# Produce a random number
my uint $rand = 0;
cblock {
    $rand = KISS();
}

print "Random number is $rand\n";

Take a moment to appreciate the following two facts: (1) Writing the module was absurdly easy, basically copying a clex block into a module file and renaming the block to cshare. (2) Importing and using the function in the module was absurdly easy, just a use statement. Inline::C has made the first of these fairly simple, and ExtUtils::Depends sorta provides a system for the second of these. C::Blocks was designed to simultaneously solve both, and I believe it provides an easier-to-use approach.

Finally, here is an illustration of the lexical scoping in action, to give you an illustration of how it really works. The symbol tables imported when you use a module are lexically scoped to the block into which you use them (just like the effect of use warnings is lexically scoped).

use strict;
use warnings;
use C::Blocks;
use C::Blocks::Types qw(uint);

my uint $rand = 0;

# Create an inner lexical scope
{
    use MyRNG;
    cblock {
        $rand = KISS();
    }
    print "First random number is $rand\n";
}

cblock {
    $rand = KISS();
}
print "Second random number is $rand\n";

Upon running this script, I got an error:

$ perl test.pl 
C::Blocks compiler error:
test.pl:18: error: undeclared function 'KISS'

If you comment out that line, it runs just fine.

Today I explained how C::Blocks makes it easy to write functions and other declarations using clex blocks. I also explained how to write a module that provides function and other declarations for other modules by using cshare blocks. This module-based C code sharing was the driving impetus for this project all along, and getting it right has been central to my work on it. Even though C::Blocks makes all of this easy, does that mean you should use it? Perl is much more expressive and you may be able to articulate your algorithmic thoughts in 1/3 the number of lines compared to a C implementation. Choosing the right tool---the right language---for the job shall be the topic of a forthcoming treat.

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

1 Comment

very cool!

Leave a comment

About David Mertens

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