Created
December 21, 2011 21:36
-
-
Save tadzik/1507821 to your computer and use it in GitHub Desktop.
Post draft
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=head1 Native libraries | |
Last year flussence++ wrote a nice post about writing XMMS bindings for | |
Perl 6 using the Native Call Interface. It has improved a bit since then, | |
(at least NCI, I don't know about XMMS), so let's show it off a bit. | |
Previously, we were carefully writing all the C subs we needed to use | |
and then usually writing some Perl 6 class which wrapped it in a nice, | |
familiar interface. That doesn't change much, except that now a class | |
is not really an interface for some C-level data structure. Thanks to | |
the new metamodel we can now make our class to actually I<be> a C-level | |
data structure, at least under the hood. Consider a class representing | |
a connection to Music Player Deamon: | |
class Connection is repr('CPointer') { | |
sub mpd_connection_new(Str $host, Int $port) | |
returns Connection | |
is native('libmpdclient.so') {} | |
sub mpd_connection_free() | |
is native('libmpdclient.so') {} | |
method new(Str $host, Int $port) { | |
self.bless(mpd_connection_new($host, $port)) | |
} | |
method DESTROY { | |
mpd_connection_free(self) | |
} | |
} | |
The first line does not neceserilly look familiar. The C<is repr> trait | |
tells the compiler that the internal representation of the class | |
C<Connection> is a C pointer. It still is a fully functional Perl 6 type, | |
which we can use in method signatures or wherever (as seen in the lines | |
below). | |
We then declare some native fuctions we're going to use. It's quite | |
convenient to put them inside the class body, so they don't pollute the | |
namespace and don't confuse the user. What we are really exposing here | |
is the C<new> method, which uses C<bless> to set the object's internal | |
representation to what C<mpd_connection_new> has returned. From now on | |
our object is a Perl 6 level object, while under the hood being a mere | |
C pointer. In method C<DESTROY> we just pass C<self> to another native | |
function, C<mpd_connection_free>, without the need to unbox it or | |
whatever. The C<NativeCall> module will just extract its internal | |
representation and pass it around. Ain't that neat? | |
Let's see some bigger example. We'll use C<taglib> library to extract | |
the metadata about some music files lying around. Let's see the C<Tag> | |
class first: | |
class Tag is repr('CPointer') { | |
sub taglib_tag_title(Tag) returns Str is native('libtag_c.so') {} | |
sub taglib_tag_artist(Tag) returns Str is native('libtag_c.so') {} | |
sub taglib_tag_album(Tag) returns Str is native('libtag_c.so') {} | |
sub taglib_tag_genre(Tag) returns Str is native('libtag_c.so') {} | |
sub taglib_tag_year(Tag) returns Int is native('libtag_c.so') {} | |
sub taglib_tag_track(Tag) returns Int is native('libtag_c.so') {} | |
sub taglib_tag_free_strings(Tag) is native('libtag_c.so') {} | |
method title { taglib_tag_title(self) } | |
method artist { taglib_tag_artist(self) } | |
method album { taglib_tag_album(self) } | |
method genre { taglib_tag_genre(self) } | |
method year { taglib_tag_year(self) } | |
method track { taglib_tag_track(self) } | |
method free { taglib_tag_free_strings(self) } | |
} | |
That one is pretty boring: plenty of native functions, and plenty of | |
methods being exactly the same things. You may have noticed the lack of | |
C<new>: how are we going to get an object and read our precious tags? | |
In C<taglib>, the actual C<Tag> object is obtained from a C<Tag_File> | |
object first. Why didn't we implement it first? Well, it's going to | |
have a method returning the C<Tag> object shown above, so it was | |
convenient to declare it first. | |
class TagFile is repr('CPointer') { | |
sub taglib_file_new(Str) returns TagFile is native('libtag_c.so') {} | |
sub taglib_file_free(TagFile) is native('libtag_c.so') {} | |
sub taglib_file_tag(TagFile) returns Tag is native('libtag_c.so') {} | |
sub taglib_file_is_valid(TagFile) returns Int | |
is native('libtag_c.so') {} | |
method new(Str $filename) { | |
unless $filename.IO.e { | |
die "File '$filename' not found" | |
} | |
my $self = self.bless(taglib_file_new($filename)); | |
unless taglib_file_is_valid($self) { | |
taglib_file_free(self); | |
die "'$filename' is invalid" | |
} | |
return $self; | |
} | |
method tag { taglib_file_tag(self) } | |
method free { taglib_file_free(self) } | |
} | |
Note how we use native functions in C<new> to check for exceptional | |
situations and react appropriately Perl 6 way. Now we only have to write | |
a simple MAIN before we can test it on our favourite music files. | |
sub MAIN($filename) { | |
my $file = TagFile.new($filename); | |
my $tag = $file.tag; | |
say 'Artist: ', $tag.artist; | |
say 'Title: ', $tag.title; | |
say 'Album: ', $tag.album; | |
say 'Year: ', $tag.year; | |
$tag.free; | |
$file.free; | |
} | |
Live demo! Everyone loves live demos. | |
$ perl6 taginfo.pl some-track.mp3 | |
Artist: Diablo Swing Orchestra | |
Title: Balrog Boogie | |
Album: The Butcher's Ballroom | |
Year: 2009 | |
Works like a charm. I promise I'll wrap it up in some nice C<Audio::Tag> | |
module and release it on Github shortly. | |
Of course there's more to do with NativeCall than just passing raw | |
pointers around. You could, for example, declare it as a | |
C<repr('CStruct')> and access the C<struct> field directly, as you would | |
in the good, old C. This is only partly implemented as for now though, | |
but that shouldn't stop you from experimenting and seeing what you can do | |
before Christmas. Happy hacking! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment