=head1 NAME

XS::Framework::Manual::recipe06 - XS::Framework advanced topic

=cut

=head1 DESCRIPTION

If you have control on C++ API you'd like to port on Perl, or the API was
designed for integration with scripting languages, then C++ objects give
dedicated or, more commonly, C<shared> ownership, i.e. they might be held
from a script or from other C++ objects together.

The most common approach to achive that is to use refcounted policy on
a C++ class. The most common approach in C++ world is to use C<std::shared_ptr>,
however, if you have access to sources, we do not recommend using it as
it provides B<non-zero costs> on its thread-safety guarantees. In Perl
it is rather exceptional use threads and do inter-thread communications,
that's why we'd recommend to avoid using C<std::shared_ptr>.

Another approach is to use L<local_shared_ptr> or L<intrusive_ptr> from
L<boost|https://www.boost.org/> library. The former is similar to
C<std::shared_ptr> without the thread-safety guarantees, i.e. optimal
for using in Perl. The later assumes that refcounted policy is injected
into C++ class itself, so it is able to do basic refcount-related operations:
C<dec>, C<inc> and get use count. To simplify additions of the operations
in C++ class the C<intrusive_ref_counter> is shipped: your class just
have to inherit from it, and the counter and the operations will appear
in the C++ class.

The L<XS::libpanda> already ships the C<Refcnt> / C<iptr> with the
similar to boost's intrusive pointer policy. Let's show how to use it

    struct TimezoneRecipe11: public panda::Refcnt {
        //                              (1)
        const char* get_name() const { return name.c_str(); }
    private:
        TimezoneRecipe11(const char* name_): name{name_} { }
        std::string name;
        friend class DateRecipe11;
    };

    using TimezoneRecipe11SP = panda::iptr<TimezoneRecipe11>;

    struct DateRecipe11: public panda::Refcnt {
        //                      (2)
        DateRecipe11(const char* tz_ = "Europe/Minsk"): tz(new TimezoneRecipe11(tz_)) { update(); }
        void update()   { epoch = std::time(nullptr); }

        int get_epoch() const { return epoch; }
        TimezoneRecipe11SP get_timezone() { return tz; }
        // (3)
    private:
        std::time_t epoch;
        TimezoneRecipe11SP tz;
        // (4)
    };

All is required to support refcount-policy on a C++ class, is just inherit from
C<panda::Refcnt> in your classes (1) and (2). Then, obviously, C++ class have
to hold smpart pointer (C<iptr>) of the dependendent class, instead of object
or object pointer or object reference. If a object embeds another object, that
means B<exclusive ownership>; if the object embeds a reference to another object,
that means <no ownership> on it; and the pointer case means either exlcusive or
no ownership. C<iptr> (or C<std::shared_ptr>) grants I<shared ownership> (4).
The operation C<get_timezone> (3) above actually does timezone sharing.

    namespace xs {
        template <>
        struct Typemap<DateRecipe11*> : TypemapObject<DateRecipe11*, DateRecipe11*, ObjectTypeRefcntPtr, ObjectStorageMG> {
            //                                                                           (5)
            static std::string package () { return "MyTest::Cookbook::DateRecipe11"; }
        };

        template <>
        struct Typemap<TimezoneRecipe11*> : TypemapObject<TimezoneRecipe11*, TimezoneRecipe11*, ObjectTypeRefcntPtr, ObjectStorageMG> {
            //                                                                                          (6)
            static std::string package () { return "MyTest::Cookbook::TimezoneRecipe11"; }
        };
    };

The typemaps for the C<Date> and C<Timezone> are rather trivial; the only
moment to note is the C<ObjectTypeRefcntPtr> lifetime policy in (5) and (6).

    MODULE = MyTest                PACKAGE = MyTest::Cookbook::TimezoneRecipe11
    PROTOTYPES: DISABLE

    const char* TimezoneRecipe11::get_name()

    MODULE = MyTest                PACKAGE = MyTest::Cookbook::DateRecipe11
    PROTOTYPES: DISABLE

    DateRecipe11* DateRecipe11::new(const char* name)

    void DateRecipe11::update()

    std::time_t DateRecipe11::get_epoch()

    TimezoneRecipe11SP DateRecipe11::get_timezone()

The xs-adapters are also pretty trivial.

Please note, that L<XS::Framework> is shipsed with typemap auto-deduction rules.
Defining typemap for type C<T>, the typemap for iptr<T> is auto-decuded.

That's not true for C<std::shared_ptr> (and for C<boost::local_shared_ptr>) as
the refcounter is stored B<outside> of an object. So, if in xs-adapter the
std::shared_ptr<T> is used, the typemap for std::shared_ptr<T> should be
defined; if, in addition, the T* pointer is used, then, in addition the
typemap<T*> for it should be defined also. Do not be confused with the shipped
C<ObjectTypeSharedPtr> lifetime policy for C<std::shared_ptr> for
C<TypemapObject>: the typemap for std::shared_ptr<T> still have to be defined,
probaby with help of C<TypemapObject>.

It is possible, that C++ class has it's own refcounted inferface. Then, to
use C<ObjectTypeRefcntPtr> lifetime policy, the following free functions must be
defined for it:

    void refcnt_inc(MyClass*);
    void refcnt_dec(MyClass*);
    std::uint32_t refcnt_get(MyClass*);

Short summary: if C++ API offers shared ownership on objects, then it is friendly
for integration into scripting languages. L<XS::libpanda> ships C<Refcnt>
and C<iptr> helpers following intrusive refcounter approach. C<ObjectTypeRefcntPtr>
lifetime policy for C<TypemapObject> helps to adopt refcounted objects into Perl.

=head1 SEE ALSO

L<https://www.boost.org/>

L<XS::libpanda>
