1. Intro to XS/C++

    Doug Bell

    Double Cluepon Software Corp.

  2. Don't Panic

  3. Just the Basics

  4. Start with h2xs

  5. What we have

    • MyModule/
    • README
    • Changes
    • MANIFEST
    • ppport.h
    • lib/MyModule.pm
    • t/MyModule.t
    • MyModule.xs
    • Makefile.PL
    • Missing: typemap
    • Missing: perlobject.map
  6. lib/MyModule.pm

    package MyModule;
    
    use 5.014001;
    use strict;
    use warnings;
    
    our @ISA = qw();
    
    our $VERSION = '0.01';
    
    require XSLoader;
    XSLoader::load('MyModule', $VERSION);
    
    # Preloaded methods go here.
    
    1;
    
  7. MyModule.xs

    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"
    
    #include "ppport.h"
    
    MODULE = MyModule               PACKAGE = MyModule
    
  8. C++ Edits to MyModule.xs

    #ifdef __cplusplus
    extern "C" {
    #endif
    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"
    #ifdef __cplusplus
    }
    #endif
    
  9. C++ typemap file

  10. typemap file

    # typemap
    MyClass *       O_OBJECT
    
  11. typemap file

    MyClass *       O_MYCLASS
    
    OUTPUT
    O_MYCLASS
        sv_setref_pv( $arg, CLASS, (void*)$var );
    
    INPUT
    O_MYCLASS
        if ( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) {
            $var = ($type)SvIV((SV*)SvRV( $arg ));
        }
        else {
            warn( \"${Package}::$func_name() -- \
    		$var not a blessed SV reference\" );
            XSRETURN_UNDEF;
        }
    
    • Acts much like macro definitions
    • CLASS created by XS new()
    • $arg replaced by Perl SV
    • $var replaced by C/C++ variable
  12. Makefile.PL

    use 5.014001;
    use ExtUtils::MakeMaker;
    WriteMakefile(
        NAME              => 'MyModule',
        VERSION_FROM      => 'lib/MyModule.pm', # finds $VERSION
        PREREQ_PM         => {}, # e.g., Module::Name => 1.1
        ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
          (ABSTRACT_FROM  => 'lib/MyModule.pm', # retrieve abstract from module
           AUTHOR         => 'Doug <madcityzen@gmail.com>') : ()),
        LIBS              => [''], # e.g., '-lm'
        DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
        INC               => '-I.', # e.g., '-I. -I/usr/include/other'
            # Un-comment this if you add C files to link with later:
        # OBJECT            => '$(O_FILES)', # link all the C files too
        ### Add these for C++ support
        CC                => 'g++', # Override from Config
        LD                => 'g++', # Override from Config
        XSOPT             => '-C++',
        TYPEMAPS          => ['perlobject.map'],
    );
    
  13. Interface a C++ Class

    #ifdef __cplusplus
    extern "C" {
    #endif
    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"
    #ifdef __cplusplus
    }
    #endif
    
  14. Interface a C++ Class

    class Card {
        int value_;
        enum t_suit { SPADES, CLUBS, HEARTS, DIAMONDS } suit_;
        public:
            Card( int value, t_suit suit ) {
                value_ = value;
                suit_ = suit;
            }
            ~Card() { }
            int value() { return value_; }
            int suit() { return suit_; }
            void set_value( int value ) {
                value_ = value;
            }
            void set_suit( t_suit suit ) {
                suit_ = t_suit;
            }
    };
    
  15. Interface a C++ Class

    MODULE = Card               PACKAGE = Card
    
    Card *
    Card::new( int suit, int value )
    
    void
    Card::DESTROY()
    
    int
    Card::value()
    
    int
    Card::suit()
    
    void
    Card::set_value( int value )
    
    void
    Card::set_suit( int suit )
    
  16. typemap

    Card *      O_OBJECT
    
  17. Build and test

    $ perl Makefile.PL && make test
    
    This space accidentally left blank
  18. More Difficult Interfaces

    #include <vector>
    class Deck {
        std::vector<Card> cards_;
        public:
            Deck() { }
            ~Deck() { }
            void add_card( Card card ) {
                cards_.push_back( card );
            }
            std::vector cards() {
                return cards_;
            }
    };
    
  19. More Difficult Interfaces

    MODULE = Card               PACKAGE = Deck
    
    Deck *
    Deck::new()
    
    void
    Deck::DESTROY()
    
  20. Custom CODE:

    void
    Deck::add_card( suit, value )
        int suit
        int value
        CODE:
            Card card( suit, value );
            THIS->add_card( card );
    
  21. Returning Arrays

    AV *
    Deck::cards()
        CODE:
            std::vector<Card> cards( THIS->cards() );
            std::vector<Card>::iterator iter( cards.begin() );
            RETVAL = newAV();               // create an array
            sv_2mortal( (SV*)RETVAL );      // "mortalize"
            for ( ; iter != cards.end(); iter++ ) {
                SV* card_sv( newSV(0) );    // create a scalar
                sv_setref_pv( card_sv, "Card", (void*)&*iter ); // bless
                av_push( RETVAL, card_sv );
            }
        OUTPUT:
            RETVAL
    
    • AV - Array Value; SV - Scalar Value
    • Mortal - delayed refcount decrement
      • If not used, will be GCed
      • my @cards = $deck->cards;
    • RETVAL - Created automatically with type
    • sv_setref_pv - Set SV to be Reference to Pointer Value
      • Bless into a package too
    • CODE: requires OUTPUT:
  22. Thems The Basics

  23. Memory Management with SVs

  24. Building shared_sv

  25. Constructor(s)

    /** Default constructor needed for STL containers */
    shared_sv() : p_(0), sv_(0) {};
    
    /** Useful constructor */
    shared_sv( void* p, std::string package )
        : p_(p), sv_( newSV(0) ) // Created with refcnt 1
    {
        if ( sv_ == 0 ) return;
        sv_ = sv_setref_pv( sv_, package.c_str(), p_ );
    }
    
  26. Copy constructor

    shared_sv( const shared_sv& that ) 
        : p_(that.p_), sv_(that.sv_)
    {
        if ( sv_ == 0 ) return; // Don't do empty SVs
        // We can increment the refcount faster if we 
        // don't care about return value (void)
        SvREFCNT_inc_void( sv_ );
    }
    
  27. Assignment operator

    shared_sv& operator=( const shared_sv& that ) {
        if ( this == &that ) return *this; // Mandatory
        p_ = that.p_;
        if ( sv_ != 0 ) 
            SvREFCNT_dec( sv_ ); // Lose our SV
        sv_ = that.sv();
        if ( sv_ == 0 ) return *this;
        SvREFCNT_inc_void( sv_ ); // Gain their SV
        return *this;
    }
    
  28. Destructor

    ~shared_sv() {
        if ( sv_ == 0 ) return;
        SvREFCNT_dec( sv_ );
    }
    
  29. Usage

    shared_sv make_card( ) {
        // Card* card = new Card(); <- bad
        shared_sv ssv( new Card(), "Card" ); // <- good
        // We return a copy
        return ssv;
        // Copy means refcount incremented
    }
    
    std::vector<shared_sv> cxx_array();
    // Card* card = new Card(); <- bad
    shared_sv ssv( new Card(), "Card" ); // <- good
    cxx_array.push_back( ssv );
    // cxx_array keeps a copy, refcount incremented
    
  30. Helpful tips

  31. Docs for C++

  32. The End!

    Slides are licensed under a CC-BY-SA 3.0 license.

    Code is licensed under the Artistic License or GNU GPL v1.0 or later (the same terms as Perl itself).