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 use
d 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 cblock
s, 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 cblock
s 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 cblock
s 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 use
d. 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.
very cool!