=head1 NAME

XS::Framework::Manual::recipe01 - XS::Framework basics

=cut

=head1 DESCRIPTION

Let's assume that there is an external C++ class, which we'd like to adapt
to Perl. Let it be the simple date structure, which encapsulates Unix epoch:

    struct DateRecipe01a {
        DateRecipe01a()  { update(false) ; }
        ~DateRecipe01a() { std::cerr << "~DateRecipe01a()\n"; }
        void update(bool trace = true)   {
            if (trace) std::cerr << "DateRecipe01a::update()\n";
            epoch = std::time(nullptr);
        }

        int get_epoch() const { return epoch; }
    private:
        std::time_t epoch;
    };

First of all there should be a B<typemap> for C<DateRecipe01a>. It is
recommended to have an external C<.h> file, however for the bravety
it is embedded into C<.xsi> file:

    #include <xs.h>             // (1)
    #include <my-library.h>     // (2)

    namespace xs {      // (3)
        template <>     // (4)
        struct Typemap<DateRecipe01a*> : TypemapObject<DateRecipe01a*, DateRecipe01a*, ObjectTypePtr, ObjectStorageIV, StaticCast> {
            //  (5)           (6)           (7)             (8)             (9)             (10)            (11)            (12)
            static std::string package () { return "MyTest::Cookbook::DateRecipe01a"; }
            //                  (13)                       (14)
        };
    }

The L<XS::Framework> C++ headers should be included as shown in (1). Then the
target library headers should be included (2).

Then the C<typemap> (aka Perl-C++ mapping rules for particular class) should be
defined: it is done as C<Typemap> class (5) full specialization (4) in the
C<xs> namespace (3). This is similar how hash-function is for
C<std::unordererd_hash> is specialized in namespace C<std> for user supplied
classes.

We decided to use B<pointer-semantics> (6), i.e. create in C++ code the
C<DateRecipe01a> objects on heap and return them to Perl. As specializing
C<Typemap> from scratch is non-trivial job, it is recommended to use
the supplied helper C<TypemapObject> (7).

There is no class inheritance in the current recipe, so basic class
C<DateRecipe01a*> (8) matches final class C<DateRecipe01a*> (9).

We decided to follow commonly used pattern in CPAN-modules, i.e. C<LifetimePolicy>
= C<ObjectTypePtr> (10), which transfers object ownernership to Perl via pointer
and invoke C<delete DateRecipe01a*> when perl wrapper goes out of life; and
C<StoragePolicy> = C<ObjectStorageIV> (11) to use integer field of perl SV*
to store pointer to C<DateRecipe01a*>. As the C++ class <DateRecipe01a*> does
not participates in virtual inheritance in accordance with C++ rules it is
possible to use C<static_cast>(12) policy for pointer; it is the fastest
approach as it has zero runtime costs.

The C<package> method (13) defines the blessed package name (14) via which
the C<DateRecipe01a> is accessible from Perl scripts.

Literally that's all typemap-code which have to be written. All other lines
are just policies, i.e. specification/specialization of C<TypemapObject>
behaviour.

Let's show xs-adapter code (stored in C<.xs/.xsi> file)

    MODULE = MyTest                PACKAGE = MyTest::Cookbook::DateRecipe01a
    #                                               (15)
    PROTOTYPES: DISABLE

    DateRecipe01a* DateRecipe01a::new() { RETVAL = new DateRecipe01a(); }
    #  (16)           (17)                  (18)         (19)

    void DateRecipe01a::update()
    # (20)

    std::time_t DateRecipe01a::get_epoch()
    # (21)

    void DateRecipe01a::DESTROY() {
        // (22)
        std::cerr << "xs-adapter MyTest::Cookbook::DateRecipe01a::DESTROY\n";
    }

Here and after extended L<XS::Install>'s syntax is used.

The standard preambula (15) for xs-adapter should exist.

Let's define constructor for xs-adapter, i.e. for perl class
C<MyTest::Cookbook::DateRecipe01a>: (16-19). By usual perl conventions
the constructor should be named C<new>, and to let XS-code know that it
belongs to C<MyTest::Cookbook::DateRecipe01a> it should be prefixed
with C<DateRecipe01a> (17). It returns the newly constructed object (16)
of C++ type C<DateRecipe01a> by pointer. The part in braces (18..19) is
optional and can be ommited (in that case it will be autogenerated for you).

The entity (20) is used for
mappping perl SV* to the special variable C<THIS> using C<xs::in>.

Lines (20..21) shows how xs-adapter proxies method calls on C++ object
if signatures (including parameters) of xs-adapter and C++ class do match.

As the C<ObjectStorageIV> has been chosed as C<StoragePolicy> and Perl
takes ownership on the C++ objects to avoid memory leaks, the underlying
C++ object (C<struct DateRecipe01a>) have to be deleted when perl wrapper
(SV*) is released. To achive that, there is no other way then to hook
on C<DESTROY> method of xs-adapter: L<XS::Framework> on XS-parse phase
silently inserts Typemap<DateRecipe01a>::destroy() call at the end of
C<DESTROY> method of xs-adapter, which is forwarded to
C<TypemapObject::destroy>, which forwards to C<ObjectTypePtr::delete>,
which actually invokes C<delete> on the object, and no memory leaks
occur.

The last piece is C<Makefile.PL>, which should look something like

    use XS::Install;

    my %params = (
        ...
        CPLUS         => 11,
        SRC           => ['src'],
        INC           => '-Isrc',
        BIN_DEPS      => 'XS::Framework',
        BIN_SHARE     => {INCLUDE  => {'src' => '/'}},
    );

    write_makefile(%params);

Please, refer L<XS::Install> documentation for details.

The correct behaviour can be seen in the C<prove -blv t/cookbook/recipe01.t>
output:

    t/cookbook/recipe01.t ..
    # date = MyTest::Cookbook::DateRecipe01a=SCALAR(0x2203578), epoch = 1545299554
    # date = MyTest::Cookbook::DateRecipe01a=SCALAR(0x2203578), epoch = 1545299554
    ok 1 - no (unexpected) warnings (via done_testing)
    DateRecipe01a::update()
    1..1
    xs-adapter MyTest::Cookbook::DateRecipe01a::DESTROY
    ~DateRecipe01a()

The C<DESTROY> method is executed in Perl C<eval> context, which leads to
significant slowdown without any needs (in our case). To avoid that it
is recommended to use C<ObjectStorageMG> storage policy. The need of
the C<DESTROY> method falls away (in fact, if there will be one, it will
lead to slowdown destruction again).

Here is an reciepe of C<ObjectStorageMG> storage policy. Source class:

    // (23)
    struct DateRecipe01b {
        DateRecipe01b()  { update(false) ; }
        ~DateRecipe01b() { std::cerr << "~DateRecipe01b()\n"; }
        void update(bool trace = true)   {
            if (trace) std::cerr << "DateRecipe01b::update()\n";
            epoch = std::time(nullptr);
        }

        int get_epoch() const { return epoch; }
    private:
        std::time_t epoch;
    };

Typemap for it:

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

And xs-adapter:

    // (25)
    MODULE = MyTest                PACKAGE = MyTest::Cookbook::DateRecipe01b
    PROTOTYPES: DISABLE

    DateRecipe01b* DateRecipe01a::new() { RETVAL = new DateRecipe01b(); }

    void DateRecipe01b::update()

    std::time_t DateRecipe01b::get_epoch()

The C<prove -blv t/cookbook/recipe01.t> output confirms, that there are no
memory leaks:

    t/cookbook/recipe01.t ..
    # date = MyTest::Cookbook::DateRecipe01b=SCALAR(0x221e480), epoch = 1545299554
    # date = MyTest::Cookbook::DateRecipe01b=SCALAR(0x221e480), epoch = 1545299554
    ok 1 - no (unexpected) warnings (via done_testing)
    DateRecipe01b::update()
    1..1
    ~DateRecipe01b()

Please note, that C++ clases C<DateRecipe01a> and C<DateRecipe01b> are identical.
In classical XS it is possible to have different aliases and, hence, different
typemaps for the same C++ class. As L<XS::Framework> uses C++ template
specializations it is not possible to have different specializations for
I<aliases>.

=cut
