XS::Framework::Manual::recipe06 - XS::Framework basics


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

    const char* Base06::method()

    Base06* Base06::new()
    #             (7)

    MODULE = MyTest                PACKAGE = MyTest::Cookbook::Derived06A

    Derived06A* Derived06A::new()
    #                   (8)

    const char* Derived06A::specific_method()
    #                   (9)

    BOOT {
        //  (10)

    MODULE = MyTest                PACKAGE = MyTest::Cookbook::Derived06B

    Derived06B* Derived06B::new()
    #               (10)

    const char* Derived06B::specific_method()
    #               (11)

    BOOT {
        //          (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.