XS::Framework::Manual::recipe06 - XS::Framework advanced topic
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, shared ownership, i.e. they might be held from a script or from other C++ objects together.
shared
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 std::shared_ptr, however, if you have access to sources, we do not recommend using it as it provides 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 std::shared_ptr.
std::shared_ptr
Another approach is to use local_shared_ptr or intrusive_ptr from boost library. The former is similar to 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: dec, inc and get use count. To simplify additions of the operations in C++ class the 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.
dec
inc
intrusive_ref_counter
The Alien::libpanda already ships the Refcnt / iptr with the similar to boost's intrusive pointer policy. Let's show how to use it
Refcnt
iptr
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 panda::Refcnt in your classes (1) and (2). Then, obviously, C++ class have to hold smpart pointer (iptr) of the dependendent class, instead of object or object pointer or object reference. If a object embeds another object, that means 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. iptr (or std::shared_ptr) grants shared ownership (4). The operation get_timezone (3) above actually does timezone sharing.
panda::Refcnt
get_timezone
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 Date and Timezone are rather trivial; the only moment to note is the ObjectTypeRefcntPtr lifetime policy in (5) and (6).
Date
Timezone
ObjectTypeRefcntPtr
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 XS::Framework is shipsed with typemap auto-deduction rules. Defining typemap for type T, the typemap for iptr<T> is auto-decuded.
T
That's not true for std::shared_ptr (and for boost::local_shared_ptr) as the refcounter is stored 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 ObjectTypeSharedPtr lifetime policy for std::shared_ptr for TypemapObject: the typemap for std::shared_ptr<T> still have to be defined, probaby with help of TypemapObject.
boost::local_shared_ptr
ObjectTypeSharedPtr
TypemapObject
It is possible, that C++ class has it's own refcounted inferface. Then, to use 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. Alien::libpanda ships Refcnt and iptr helpers following intrusive refcounter approach. ObjectTypeRefcntPtr lifetime policy for TypemapObject helps to adopt refcounted objects into Perl.
https://www.boost.org/
Alien::libpanda
To install XS::Framework, copy and paste the appropriate command in to your terminal.
cpanm
cpanm XS::Framework
CPAN shell
perl -MCPAN -e shell install XS::Framework
For more information on module installation, please visit the detailed CPAN module installation guide.