NAME
XS::Framework::Manual::recipe06 - XS::Framework basics
DESCRIPTION
Let's assume that the following type hierarchy in C++:
struct Base06 {
virtual const char* method() { return "from base"; }
virtual ~Base06() { }
};
struct Derived06A: public Base06 {
virtual const char* method() { return "from derived-A"; }
const char* specific_method() { return "specific-A"; }
};
struct Derived06B: public Base06 {
virtual const char* method() { return "from derived-B"; }
const char* specific_method() { return "specific-B"; }
};
The typemaps should also reflect the hierarchy as:
namespace xs {
template <typename D>
// (1)
struct Typemap<Base06*, D> : TypemapObject<Base06*, D, ObjectTypePtr, ObjectStorageMG, StaticCast> {
// (2) (3)
static std::string package () { return "MyTest::Cookbook::Base04"; }
// (4)
};
template <> struct Typemap<Derived06A*> : Typemap<Base06*, Derived06A*> {
// (5)
static std::string package () { return "MyTest::Cookbook::Derived06A"; }
};
template <> struct Typemap<Derived06B*> : Typemap<Base06*, Derived06B*> {
// (6)
static std::string package () { return "MyTest::Cookbook::Derived06B"; }
};
}
The base type in C++, which has to be base class in Perl too, have to be defined as partial template specialization (1). This is important moment: the partial specialization for base class defines typemap for base class itself and for all derived classes. This is done when template parameter D
is substituted in (2) and (3) either with Base06
class or any child (grand-child, grand-grand-...-child) derived class.
The base typemap also defines and fixes life time, storage and casting policies for all hierarchy. In almost all cases this follows DWIM principle.
The default package name (4) is used for base class on xs::out
when no hint
parameter is supplied.
The derived classes, as they are final (i.e. no other derived classes expected), should do full specialization of typemap and inherit the specialized base typemap (5), (6). If the derived classes are non final, they should be patrially specialized. In other words, if you are writing library, which is supposed to be extended in XS/C++, then, make your typemaps extendable (partially specialized).
That complexity with typemap<B, D> is needed to perform fast casting right from Base* pointer to DerivedN* pointer, ommiting all intermediate classes.
The xs-adapters will be:
MODULE = MyTest PACKAGE = MyTest::Cookbook::Base06
PROTOTYPES: DISABLE
const char* Base06::method()
Base06* Base06::new()
# (7)
MODULE = MyTest PACKAGE = MyTest::Cookbook::Derived06A
PROTOTYPES: DISABLE
Derived06A* Derived06A::new()
# (8)
const char* Derived06A::specific_method()
# (9)
BOOT {
Stash(__PACKAGE__).inherit("MyTest::Cookbook::Base06");
// (10)
}
MODULE = MyTest PACKAGE = MyTest::Cookbook::Derived06B
PROTOTYPES: DISABLE
Derived06B* Derived06B::new()
# (10)
const char* Derived06B::specific_method()
# (11)
BOOT {
Stash(__PACKAGE__).inherit("MyTest::Cookbook::Base06");
// (12)
}
It is possible to use short-cut constructors (7), (8), (10). It will automatically call the base constructor of the related class and forward all parameters. All constructors for all XS-adapters have to be defined, as they are not inherited.
The xs-adapters should also reflect classes hierarhy. That way the adapted method
can be written once in base xs-adapter only (assuming, that no additional/specific actions in XS-adapter are needed). That way the following code works as expected:
MyTest::Cookbook::Derived06A->new->method; # => "from derived-A"
The xs-hierarhy is defined as hierarchy between corresponding stashes at BOOT
method (10), (12). The macros __PACKAGE__
is substituted to C string literal of the most recently parsed PACKAGE
preambula.
That's way there is need to define only class-specific methods (9), (11)
The short summary: typemaps for base classes should be extensible (patrially specialized) and typemaps and xs-adapters should reflect original C++ hierarchy.