=head1 NAME

XS::Framework::Manual::recipe04 - XS::Framework basics



Let's assume that we have C<Timezone> object, which is available via C<std::shared_ptr>.

    struct TimezoneRecipe04 {
        const char* get_name() const { return name.c_str(); }
        static std::shared_ptr<TimezoneRecipe04> create(const char* name) {
            return std::make_shared<TimezoneRecipe04>(name);
        TimezoneRecipe04(const char* name_): name{name_} { }
        ~TimezoneRecipe04() { std::cerr << "~TimezoneRecipe04()\n"; }
        std::string name;

    using TimezoneRecipe04SP = std::shared_ptr<TimezoneRecipe04>;
    // (1)

It is recommended to have an alias(1) for smart_pointes, as there will be less
code below. Let's define typemap for the shared pointer of of Timezone:

    namespace xs {
        template <>
        struct Typemap<TimezoneRecipe04SP> : TypemapObject<TimezoneRecipe04SP, TimezoneRecipe04SP, ObjectTypeSharedPtr, ObjectStorageMG, StaticCast> {
            //              (1)                                     (2)             (3)                 (4)
            static std::string package () { return "MyTest::Cookbook::TimezoneRecipe04"; }

As usual, we have to do full specialization (1) for the smart pointer; we are using
C<TypemapObject> helper and as there is no types hierarchy base class (2) and final
class (3) point to the same type (timezone smart pointer). The shipped
C<ObjectTypeSharedPtr> life time policy should be used, as it is aware of
C<std::shared_ptr> and knows how to increase and decrease it's reference counter.

The xs-adapter will be like:

    MODULE = MyTest                PACKAGE = MyTest::Cookbook::TimezoneRecipe04

    const char* get_name(TimezoneRecipe04SP tz) { RETVAL = tz->get_name(); }
    //              (5)

    TimezoneRecipe04SP create(const char* name) { RETVAL = TimezoneRecipe04::create(name); }

Pay attention to the XS-function definition (5). It would be error to write it as
C<const char* TimezoneRecipe04::get_name>, because C<XS::Framework::ParseXS> will
try to map C<THIS> variable to C<TimezoneRecipe04*>, i.e. timezone pointer; but
there is no typemap exist for C<TimezoneRecipe04*>, there is only typemap for
C<TimezoneRecipe04SP>, and C++ compiler will emit error.

For the sake of completeness there is another mapped C++ class, which uses
C<TimezoneRecipe04SP>. There is nothing new for a reader familiar with the
previous recipes.

    // C++ class
    struct DateRecipe04 {
        DateRecipe04()  { update() ; }
        void update()   { epoch = std::time(nullptr); }

        int get_epoch() const { return epoch; }
        void set_timezone(TimezoneRecipe04SP tz_) { tz = tz_; }
        TimezoneRecipe04SP get_timezone() { return tz; }
        std::time_t epoch;
        TimezoneRecipe04SP tz;

    // typemap
    namespace xs {
        template <>
        struct Typemap<DateRecipe04*> : TypemapObject<DateRecipe04*, DateRecipe04*, ObjectTypePtr, ObjectStorageMG, StaticCast> {
            static std::string package () { return "MyTest::Cookbook::DateRecipe04"; }

    // XS-adapter
    MODULE = MyTest                PACKAGE = MyTest::Cookbook::DateRecipe04

    DateRecipe04* DateRecipe04::new() { RETVAL = new DateRecipe04(); }

    void DateRecipe04::update()

    std::time_t DateRecipe04::get_epoch()

    TimezoneRecipe04SP DateRecipe04::get_timezone()

    void DateRecipe04::set_timezone(TimezoneRecipe04SP tz)

If there will be test code like:

    my $tz = MyTest::Cookbook::TimezoneRecipe04::create('Europe/Minsk');
    my $date = MyTest::Cookbook::DateRecipe04->new;
    is $date->get_timezone->get_name, 'Europe/Minsk';   # (6)
    is $date->get_timezone, $tz;                        # (7)

the check code on line (6) will pass, and on the line (7) will fail. This is because
every time new wrapper (SV*) is created by C<typemap::out> method. To handle that
you can either a) overload C<eq> method to let it extracts actual pointers from
C<shared_ptr> and does pointer comparison, or use typemaps with C<backrefs> support,
which are covered in future recipes.