XS::TNGThe current status of Perl-C bindingShmuelFombergYAPC Tokyo 2010
We have XS, don’t we?XS is hardNot true. The syntax is simpleXS is not hardBut when writing XS you are using:XS syntaxPerl gutsTypesmapsCLearning all these in the same time, is hard
We are not going to talk about XSXS is a known problemA lot of documentation on the webMost of it outdated or just wrongWe will talk about:Ctypes– calling C libraries from PerlLibperl++ - embedding and extending Perl using C++ libraryXS++  - XS – C++ binding
Who am IA Perl programmer from IsraelI’m here learning JapaneseJust started a three month courseMy first YAPC talkSo, yoroshekoonegaishimasu
What is Ctypes?Port from Python’s CtypesGood idea / interfaceStill in makingNot on CPAN yetEnable to call C functions inside DLLs, without making XS / intermediate DLL
CtypesNow let’s see some code:use Ctypes;print chr CDLL->msvcrt->toupper({sig=>"cii"})->(ord("y"));# prints ‘Y’# a more verbose style:my $func = Ctypes::Function->new  ( { lib    => 'msvcrt',      name   => 'toupper',      argtypes => 'ii',      restype  => 'c' } );print chr $func->(ord("y"));# prints ‘Y’
CtypesLet’s see more:my $func = WinDLL->user32->                              MessageBoxA({sig=>"sipppI"});$func->(0, "Hi", "BoxBox", 0);
Ctypes – built-in typesSignature details:First char – call type. ‘s’ for stdcall, ‘c’ for cdecl.Second char – return typeThe rest - arguments v =>  c_void,c =>  c_byte,C =>  c_char,s =>  c_short,S =>  c_ushort,i =>  c_int,I =>  c_uint,l =>  c_long,L =>  c_ulong,f =>  c_float,d =>  c_double,D =>  c_longdouble,p =>  c_void_p,
Building types - Simplemy $array = Array( c_ushort, [ 1, 2, 3, 4, 5 ] );$array->[2] == 3;my $multi = Array( $array, $array2, $array3 );  # multidimensional my $ushort = c_ushort(25);my $ushortp = Pointer($ushort);$$ushortp == $ushort;${$$ushortp} == 25;
Building types – Ad-hocmy $struct = Struct([  f1 => c_char('P'),  f2 => c_int(10),  f3 => c_long(90000),]);$struct->size == Ctypes::sizeof('c') + Ctypes::sizeof('i') + Ctypes::sizeof('l');$$struct->{f2} == 10;my $alignedstruct = Struct({  fields => [   o1 => c_char('Q'),   o2 => c_int(20),   o3 => c_long(180000),  ],  align => 4,});
Building types – Callbacksub cb_func {  my( $ay, $bee ) = @_; return $ay <=> $bee;}my $qsort = Ctypes::Function->new  ( { lib       => 'c',        name   => 'qsort',   argtypes => 'piip‘,  restype => 'v‘,     } );$cb = Ctypes::Callback->new( \&cb_func, 'i', ‘ss');my $disarray = Array(c_short , [2, 4, 5, 1, 3] );$qsort->($disarray, $#$disarray+1, Ctypes::sizeof('s'), $cb->ptr);$disarray->_update_;$disarray->[2] == 3
Ctypes – Libraries typesCDLLReturn type ‘i’, calling style ‘cdecl’WinDLLReturn type ‘i’, calling style ‘stdcall’OleDLLCalling style “stdcall”Return type HRESULTWill throw exception automatically on error
Ctypes – Libraries typesPerlDLLFor calling Perl XS functionsprovides XS environment for the called functionChecks the Perl error flag after the callIf there was an exception – re-throws
Libperl++Extending and Embedding PerlThe goal: to let C++ do the hard work for youReference countingCall stack operationsType conversionsException conversionshttp://github.com/Leont/libperl--
Libperl++ - EmbeddingLet’s see some code:Interpreter universe;universe.use("Carp", 1.000); bool success = universe.eval("print qq{Hello World\n}"); # acessing / creating global variables universe.scalar("status") = 404;universe.hash("table").exists("hello"); # creates @Module::data :Array data = universe.array("Module::data");
Libperl++ - ExtendingLet’s say we have this C++ code:class player {     string get_name() const;     pair<int, int> get_position() const;     void set_position(pair<int, int>);     double get_strength() const;     void set_strength(double);     void die(string); };
Libperl++ - ExtendingIt can be exported like this:Class<player> player_class = universe.add_class("Player"); player_class.add("get_name", &player::get_name); player_class.add("get_position", &player::get_position); player_class.add("set_position", &player::set_position); player_class.add("get_strength", &player::get_strength); player_class.add("set_strength", &player::set_strength); player_class.add("die", &player::die); The first line connects a C++ class “player” with Perl class “Player”, then we add each method
Libperl++ - ExtendingOf course, we can add a plain (non-object) modulePackage somewhere = universe.package("foo"); somewhere.add("other_name", some_function);
Libperl++ - ExtendingAnd the Perl side:package Extend;use strict;use warnings;use Exporter 5.57 qw/import/;our @EXPORT_OK = qw/hello/;use Perlpp::Extend;1;
Libperl++ - Calling PerlYou really don’t want to know how much work is to call Perl function from CHere is how to do it with Libperl++:Ref<Code> some_function = universe.code("some_function"); some_function("Hello World"); // in scalar contextsome_function.list("Hello World"); // in list context
Libperl++ - Calling PerlAlso works for objects:Ref<Any> testr = universe.package("Tester").call("new", 1);testr.call("print");testr.call("set", 3);testr.call("print");// automatically calls ‘DESTROY’
Libperl++ - ScalarsScalar value = universe.value_of(1);value += 2;// can use all normal operators: + - * / % == > < =value = "1";  // the same as setting to 1String value = universe.value_of("test");value == std::string("test"); // convertible to/from std::stringvalue.replace(2, 2, value); // now contains ‘tetest’value.insert(2, "st"); // now contains ‘testtest’value = "test"; // simple assignment
Libperl++ - Arraysfirstmaxminshuffledsumanyallnonebegin/endrbegin/rendpushpopshiftunshiftreverseremoveexistslengthcleareacheach_indexmapgrepreduceArray bar = universe.list("a", "b");int length = bar.length();cout << "bla is " << baz[1] << endl; bar[4] = "e"; void test(const Scalar::Base& val);std::for_each(bar.begin(), bar.end(), test);int converter(int arg);Array singles = universe.list(1, 2, 3, 4); singles.map(converter).each(test);
Libperl++ - HashsinsertexistseraseclearlengtheachkeysvaluesHash map = universe.hash();map["test"] = "duck";map["foo" ] = "bar";void printer(const char *key,                     const Scalar::Base& value); map.each(printer);
Libperl++ - ReferencesAny Perl variable support take_ref()Ref<Scalar> value = universe.value_of(1).take_ref();Reference can be specific or genericRef<Scalar>, Ref<Integer>, Ref<Number>, Ref<Any>, Ref<Hash>Reference tries to give operations shortcuts to the contained elementRef<Hash> ref = hash.take_ref();ref["foo"] = "rab";is_objectisais_exactlyweakenblessget_classname
Libperl++ - custom type convertionstructmy_type {int value;my_type(int _value) : value(_value) {}};namespace perl {   namespace typecast {       template<> structtypemap<my_type> {            static my_typecast_to(const Scalar::Base& value) {                  return my_type(value.int_value());            }typedef boost::true_typefrom_type;            static intcast_from(Interpreter&, const my_type& variable) {                 return variable.value;            }       };   }}Signals that this type can be converted to Perl typeCan construct any Perl type, (using the interpreter) or any type convertible
ExtUtils::XSppXS does not handle objectsOnly functions and constantsYou can write the object binding yourselfMuch funXS++ fills the gap“transparent C++ objects in Perl”
ExtUtils::XSppIt is still XSSo you need to know all the XS keywords, Perl internals and typemapsPlus adds its own little languageEverything is translated back to XS for compilationModule::Build::WithXSppSee ExtUtils::XSpp’s example
XS++ - XSpp::Example.pmpackage XSpp::Example; use strict; use warnings; our $VERSION = '0.01'; require XSLoader; XSLoader::load('XSpp::Example', $VERSION); 1;
XS++ - C++ headersclass Animal { public:     Animal(const std::string& name);     void SetName(const std::string& newName);     std::string GetName() const;     void MakeSound() const; private:     std::string fName; }; class Dog : public Animal { public:     Dog(const std::string& name);     void Bark() const;     void MakeSound() const; };
XS++ - xsp file#include "Animals.h" %module{XSpp::Example}; class Animal {     %name{new} Animal(std::string& name);     ~Animal();     void MakeSound();     void SetName(std::string& newName);     std::string GetName(); }; class Dog : public Animal {     %name{new} Dog(std::string& name);     ~Dog();     void MakeSound();     void Bark(); };
XS++ - Dog.cc#include "Animals.h"Dog::Dog(const std::string& name) : Animal(name) {} void Dog::Bark() const { cout << "Woof" << endl; } void Dog::MakeSound() const {     Bark(); }
XS++ - Build.PL#!/usr/bin/perl -w use strict; use Module::Build::WithXSpp; my $build = Module::Build::WithXSpp->new( module_name => 'XSpp::Example',     license => 'perl',     requires => {},     # Provides extra C typemaps for opaque objects: extra_typemap_modules => {         'ExtUtils::Typemap::ObjectMap' => '0.01',     }, );$build->create_build_script;
XS++ - mytype.mapAnimal*  O_OBJECTDog*  O_OBJECTstd::string*  T_STRINGPTR std::string  T_STRING INPUT T_STRING     $var = std::string(SvPV_nolen($arg)) T_STRINGPTR     $var = new std::string(SvPV_nolen($arg)) OUTPUT T_STRING     $arg = newSVpvn($var.c_str(), $var.length()); T_STRINGPTR     $arg = newSVpvn($var->c_str(), $var->length());Declare these types as objects to export to Perl space
More about typemapsExtUtils::Typemap::DefaultAnd the other modules in that distribution
Thank you
Building LibffiDownload from: http://github.com/atgreen/libffiTarget platform: Strawberry Perl 5.12Needed tools: cygwin with make, without gccTo build:copy src/x86/ffitarget.h to include/ffitarget.hrun bashrun configurerun makeIt will produce binaries in the “.lib” directory:Libffi.* => strewberryperl/c/libcygffi-5.dll => strewberryperl/c/bin
Building CtypesDownload from: http://gitorious.org/perl-ctypeshttp://socghop.appspot.com/gsoc/student_project/show/google/gsoc2010/tpf/t127230763807Make sure that cygwin is not availableperl Makefile.pl, dmake – test – install
Building Libperl++You need C++ Boosthttp://www.boost.org/But only the headersLibperl++ is a heavy C++ templates userUsing Module::Build style installerperl Build.PLperl Buildperl Build test perl Build install
Building types - Pointpackage t_POINT;use strict;use warnings;use Ctypes;use Ctypes::Type::Struct;our @ISA = qw|Ctypes::Type::Struct|;our $_fields_ = [ ['x',c_int], ['y',c_int], ];sub new {      my $class = ref($_[0]) || $_[0];   shift;      my $self = $class->SUPER::new({fields => $_fields_, values => [ @_ ] });      return bless $self => $class if $self;}
Building types - Pointmy $point = new t_POINT( 30, 40 );$$point->{x} == 30;$$point->{y} == 40;$$point->[0] == 30;$$point->[1] == 40; # yes, it is overloadedmy $point_2 = new t_POINT([ y => 30, x => 40 ]);my $point_3 = new t_POINT([ y => 50 ]);

C to perl binding

  • 1.
    XS::TNGThe current statusof Perl-C bindingShmuelFombergYAPC Tokyo 2010
  • 2.
    We have XS,don’t we?XS is hardNot true. The syntax is simpleXS is not hardBut when writing XS you are using:XS syntaxPerl gutsTypesmapsCLearning all these in the same time, is hard
  • 3.
    We are notgoing to talk about XSXS is a known problemA lot of documentation on the webMost of it outdated or just wrongWe will talk about:Ctypes– calling C libraries from PerlLibperl++ - embedding and extending Perl using C++ libraryXS++ - XS – C++ binding
  • 4.
    Who am IAPerl programmer from IsraelI’m here learning JapaneseJust started a three month courseMy first YAPC talkSo, yoroshekoonegaishimasu
  • 5.
    What is Ctypes?Portfrom Python’s CtypesGood idea / interfaceStill in makingNot on CPAN yetEnable to call C functions inside DLLs, without making XS / intermediate DLL
  • 6.
    CtypesNow let’s seesome code:use Ctypes;print chr CDLL->msvcrt->toupper({sig=>"cii"})->(ord("y"));# prints ‘Y’# a more verbose style:my $func = Ctypes::Function->new ( { lib => 'msvcrt', name => 'toupper', argtypes => 'ii', restype => 'c' } );print chr $func->(ord("y"));# prints ‘Y’
  • 7.
    CtypesLet’s see more:my$func = WinDLL->user32-> MessageBoxA({sig=>"sipppI"});$func->(0, "Hi", "BoxBox", 0);
  • 8.
    Ctypes – built-intypesSignature details:First char – call type. ‘s’ for stdcall, ‘c’ for cdecl.Second char – return typeThe rest - arguments v => c_void,c => c_byte,C => c_char,s => c_short,S => c_ushort,i => c_int,I => c_uint,l => c_long,L => c_ulong,f => c_float,d => c_double,D => c_longdouble,p => c_void_p,
  • 9.
    Building types -Simplemy $array = Array( c_ushort, [ 1, 2, 3, 4, 5 ] );$array->[2] == 3;my $multi = Array( $array, $array2, $array3 ); # multidimensional my $ushort = c_ushort(25);my $ushortp = Pointer($ushort);$$ushortp == $ushort;${$$ushortp} == 25;
  • 10.
    Building types –Ad-hocmy $struct = Struct([  f1 => c_char('P'),  f2 => c_int(10),  f3 => c_long(90000),]);$struct->size == Ctypes::sizeof('c') + Ctypes::sizeof('i') + Ctypes::sizeof('l');$$struct->{f2} == 10;my $alignedstruct = Struct({  fields => [   o1 => c_char('Q'),   o2 => c_int(20),   o3 => c_long(180000),  ],  align => 4,});
  • 11.
    Building types –Callbacksub cb_func {  my( $ay, $bee ) = @_; return $ay <=> $bee;}my $qsort = Ctypes::Function->new  ( { lib    => 'c', name   => 'qsort',   argtypes => 'piip‘, restype => 'v‘, } );$cb = Ctypes::Callback->new( \&cb_func, 'i', ‘ss');my $disarray = Array(c_short , [2, 4, 5, 1, 3] );$qsort->($disarray, $#$disarray+1, Ctypes::sizeof('s'), $cb->ptr);$disarray->_update_;$disarray->[2] == 3
  • 12.
    Ctypes – LibrariestypesCDLLReturn type ‘i’, calling style ‘cdecl’WinDLLReturn type ‘i’, calling style ‘stdcall’OleDLLCalling style “stdcall”Return type HRESULTWill throw exception automatically on error
  • 13.
    Ctypes – LibrariestypesPerlDLLFor calling Perl XS functionsprovides XS environment for the called functionChecks the Perl error flag after the callIf there was an exception – re-throws
  • 14.
    Libperl++Extending and EmbeddingPerlThe goal: to let C++ do the hard work for youReference countingCall stack operationsType conversionsException conversionshttp://github.com/Leont/libperl--
  • 15.
    Libperl++ - EmbeddingLet’ssee some code:Interpreter universe;universe.use("Carp", 1.000); bool success = universe.eval("print qq{Hello World\n}"); # acessing / creating global variables universe.scalar("status") = 404;universe.hash("table").exists("hello"); # creates @Module::data :Array data = universe.array("Module::data");
  • 16.
    Libperl++ - ExtendingLet’ssay we have this C++ code:class player { string get_name() const; pair<int, int> get_position() const; void set_position(pair<int, int>); double get_strength() const; void set_strength(double); void die(string); };
  • 17.
    Libperl++ - ExtendingItcan be exported like this:Class<player> player_class = universe.add_class("Player"); player_class.add("get_name", &player::get_name); player_class.add("get_position", &player::get_position); player_class.add("set_position", &player::set_position); player_class.add("get_strength", &player::get_strength); player_class.add("set_strength", &player::set_strength); player_class.add("die", &player::die); The first line connects a C++ class “player” with Perl class “Player”, then we add each method
  • 18.
    Libperl++ - ExtendingOfcourse, we can add a plain (non-object) modulePackage somewhere = universe.package("foo"); somewhere.add("other_name", some_function);
  • 19.
    Libperl++ - ExtendingAndthe Perl side:package Extend;use strict;use warnings;use Exporter 5.57 qw/import/;our @EXPORT_OK = qw/hello/;use Perlpp::Extend;1;
  • 20.
    Libperl++ - CallingPerlYou really don’t want to know how much work is to call Perl function from CHere is how to do it with Libperl++:Ref<Code> some_function = universe.code("some_function"); some_function("Hello World"); // in scalar contextsome_function.list("Hello World"); // in list context
  • 21.
    Libperl++ - CallingPerlAlso works for objects:Ref<Any> testr = universe.package("Tester").call("new", 1);testr.call("print");testr.call("set", 3);testr.call("print");// automatically calls ‘DESTROY’
  • 22.
    Libperl++ - ScalarsScalarvalue = universe.value_of(1);value += 2;// can use all normal operators: + - * / % == > < =value = "1"; // the same as setting to 1String value = universe.value_of("test");value == std::string("test"); // convertible to/from std::stringvalue.replace(2, 2, value); // now contains ‘tetest’value.insert(2, "st"); // now contains ‘testtest’value = "test"; // simple assignment
  • 23.
    Libperl++ - Arraysfirstmaxminshuffledsumanyallnonebegin/endrbegin/rendpushpopshiftunshiftreverseremoveexistslengthcleareacheach_indexmapgrepreduceArraybar = universe.list("a", "b");int length = bar.length();cout << "bla is " << baz[1] << endl; bar[4] = "e"; void test(const Scalar::Base& val);std::for_each(bar.begin(), bar.end(), test);int converter(int arg);Array singles = universe.list(1, 2, 3, 4); singles.map(converter).each(test);
  • 24.
    Libperl++ - HashsinsertexistseraseclearlengtheachkeysvaluesHashmap = universe.hash();map["test"] = "duck";map["foo" ] = "bar";void printer(const char *key, const Scalar::Base& value); map.each(printer);
  • 25.
    Libperl++ - ReferencesAnyPerl variable support take_ref()Ref<Scalar> value = universe.value_of(1).take_ref();Reference can be specific or genericRef<Scalar>, Ref<Integer>, Ref<Number>, Ref<Any>, Ref<Hash>Reference tries to give operations shortcuts to the contained elementRef<Hash> ref = hash.take_ref();ref["foo"] = "rab";is_objectisais_exactlyweakenblessget_classname
  • 26.
    Libperl++ - customtype convertionstructmy_type {int value;my_type(int _value) : value(_value) {}};namespace perl { namespace typecast { template<> structtypemap<my_type> { static my_typecast_to(const Scalar::Base& value) { return my_type(value.int_value()); }typedef boost::true_typefrom_type; static intcast_from(Interpreter&, const my_type& variable) { return variable.value; } }; }}Signals that this type can be converted to Perl typeCan construct any Perl type, (using the interpreter) or any type convertible
  • 27.
    ExtUtils::XSppXS does nothandle objectsOnly functions and constantsYou can write the object binding yourselfMuch funXS++ fills the gap“transparent C++ objects in Perl”
  • 28.
    ExtUtils::XSppIt is stillXSSo you need to know all the XS keywords, Perl internals and typemapsPlus adds its own little languageEverything is translated back to XS for compilationModule::Build::WithXSppSee ExtUtils::XSpp’s example
  • 29.
    XS++ - XSpp::Example.pmpackageXSpp::Example; use strict; use warnings; our $VERSION = '0.01'; require XSLoader; XSLoader::load('XSpp::Example', $VERSION); 1;
  • 30.
    XS++ - C++headersclass Animal { public: Animal(const std::string& name); void SetName(const std::string& newName); std::string GetName() const; void MakeSound() const; private: std::string fName; }; class Dog : public Animal { public: Dog(const std::string& name); void Bark() const; void MakeSound() const; };
  • 31.
    XS++ - xspfile#include "Animals.h" %module{XSpp::Example}; class Animal { %name{new} Animal(std::string& name); ~Animal(); void MakeSound(); void SetName(std::string& newName); std::string GetName(); }; class Dog : public Animal { %name{new} Dog(std::string& name); ~Dog(); void MakeSound(); void Bark(); };
  • 32.
    XS++ - Dog.cc#include"Animals.h"Dog::Dog(const std::string& name) : Animal(name) {} void Dog::Bark() const { cout << "Woof" << endl; } void Dog::MakeSound() const { Bark(); }
  • 33.
    XS++ - Build.PL#!/usr/bin/perl-w use strict; use Module::Build::WithXSpp; my $build = Module::Build::WithXSpp->new( module_name => 'XSpp::Example', license => 'perl', requires => {}, # Provides extra C typemaps for opaque objects: extra_typemap_modules => { 'ExtUtils::Typemap::ObjectMap' => '0.01', }, );$build->create_build_script;
  • 34.
    XS++ - mytype.mapAnimal* O_OBJECTDog* O_OBJECTstd::string* T_STRINGPTR std::string T_STRING INPUT T_STRING $var = std::string(SvPV_nolen($arg)) T_STRINGPTR $var = new std::string(SvPV_nolen($arg)) OUTPUT T_STRING $arg = newSVpvn($var.c_str(), $var.length()); T_STRINGPTR $arg = newSVpvn($var->c_str(), $var->length());Declare these types as objects to export to Perl space
  • 35.
    More about typemapsExtUtils::Typemap::DefaultAndthe other modules in that distribution
  • 36.
  • 37.
    Building LibffiDownload from:http://github.com/atgreen/libffiTarget platform: Strawberry Perl 5.12Needed tools: cygwin with make, without gccTo build:copy src/x86/ffitarget.h to include/ffitarget.hrun bashrun configurerun makeIt will produce binaries in the “.lib” directory:Libffi.* => strewberryperl/c/libcygffi-5.dll => strewberryperl/c/bin
  • 38.
    Building CtypesDownload from:http://gitorious.org/perl-ctypeshttp://socghop.appspot.com/gsoc/student_project/show/google/gsoc2010/tpf/t127230763807Make sure that cygwin is not availableperl Makefile.pl, dmake – test – install
  • 39.
    Building Libperl++You needC++ Boosthttp://www.boost.org/But only the headersLibperl++ is a heavy C++ templates userUsing Module::Build style installerperl Build.PLperl Buildperl Build test perl Build install
  • 40.
    Building types -Pointpackage t_POINT;use strict;use warnings;use Ctypes;use Ctypes::Type::Struct;our @ISA = qw|Ctypes::Type::Struct|;our $_fields_ = [ ['x',c_int], ['y',c_int], ];sub new { my $class = ref($_[0]) || $_[0]; shift; my $self = $class->SUPER::new({fields => $_fields_, values => [ @_ ] }); return bless $self => $class if $self;}
  • 41.
    Building types -Pointmy $point = new t_POINT( 30, 40 );$$point->{x} == 30;$$point->{y} == 40;$$point->[0] == 30;$$point->[1] == 40; # yes, it is overloadedmy $point_2 = new t_POINT([ y => 30, x => 40 ]);my $point_3 = new t_POINT([ y => 50 ]);