XS-Framework

 view release on metacpan or  search on metacpan

lib/XS/Framework/Manual/recipe07.pod  view on Meta::CPAN

=head1 NAME

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

=cut

=head1 C3 mixin introduction

Let's assume there is a Server with core functions defined in basic class.

    package MyBase {
        sub new {
            my $class = shift;
            return bless {} => $class;
        };
        sub on_client {
            my ($self, $client) = @_;
            print "MyBase::on_client\n";
            if ($client->{status} eq 'authorized'){ $client->{send} = '[welcome]' }
            elsif ($client->{status} eq 'not_authorized') { $client->{send} = '[disconnect]' };
        }
    }

The package is responsible for constructing object and send to client either
C<[welcome]> string upon successful login and C<[disconnect]> upon login falure.
It is desirable to have dedicated logging and authorizing components.

    package MyLogger {
        use base qw/MyBase/;    # (1)

        sub new {
            my $class = shift;
            my $obj = $class->next::method(@_) // {};   # (2)
            return bless $obj => $class;
        }
        sub on_client {
            my ($self, $client) = @_;
            print "MyLogger::on_client\n";  # (3)
            print "client ", $client->{id}, ", status = ", $client->{status}, "\n";
            $self->next::method($client);   # (4)
            print "client ", $client->{id}, ", status = ", $client->{status}, "\n";
        }
    }

    package MyAuth {
        use base qw/MyBase/;    # (5)

        sub new {
            my $class = shift;
            my $obj = $class->next::method(@_) // {};   # (6)
            return bless $obj => $class;
        }
        sub on_client {
            my ($self, $client) = @_;
            print "MyAuth::on_client\n";    # (7)
            if ($client->{id} < 0) { $client->{status} = 'not_authorized'; }
            else { $client->{status} = 'authorized'; }
            $self->next::method($client);   # (8)
        }
    };

C<MyBase> class is used as interface; I B<have to define> it's interface method
(C<on_client> in our case), which might be empty. The C<MyLogger> and C<MyAuth>
are designed as plugins, which might intercept/proxy method of base class. To
be sure that some basic implementation is still exist I<beyond> them, they
do inrerit from C<MyBase> (1), (5).

As with classical inheritance in Perl, the code should be aware of derived
classes in (2) and (6), but as the subroutines do nothing here they can be
omitted. The lines (3), (4), (7) are inserted for trace purposes.

There might be different policies how to forward to next method: in C<MyLogger>
it mimics C<around> like in L<Class::Method::Modifiers>, but it is possible
to have C<before> and C<after>.

The real magic happens in C<next::method> (4) and (8): the plugins forwards
call either to base class C<MyBase> or to next plugin. This is not known
at the place of invocation and it is defined at place, where plug-ins are
inherited, i.e. in the I<gather point>:

    package MyXServer {
        use base qw/MyLogger MyAuth MyBase/;    # (9)
        sub new {
            my $class = shift;
            my $obj = $class->next::method(@_) // {};
            return bless $obj => $class;
        }
    };

In (9) it is said that C<MyLogger> plugin's interceptors are executed first,
then C<MyAuth> interceptors are executed, and only then the generic (may be
empty) methods of C<MyBase> will be executed. C3/mro resolves multiple
inheritance problem, i.e. linearizes inheritance tree from most specific
(child) to the most generic (parent) classes.

The sample code

    my $client = {status => 'connected', id => 10};
    my $server = MyXServer->new;
    $server->on_client($client);

will output

    MyLogger::on_client
    client 10, status = connected
    MyAuth::on_client
    MyBase::on_client
    client 10, status = authorized

i.e. it works as expected.

=head1 C3 mixin using XS::Framework

Let's have this mixin logic in XS. The underlying idea is to have independent
C++ classes, which have very fast implementation, which will be bound into
XS-hierarchy. It is important to note, that C++ classes do not form hierarchy
- it is created only on XS level.

Let's suppose that there are the following C++ classes:

    enum class Status07 { CONNECTED = 1, AUTHORIZED = 2, NOT_AUTHORIZED = 3, DISCONNECTED = 4 };

    struct Client07 {
        int id;
        Status07 status;

        Client07 (int id_): id{id_}, status{Status07::CONNECTED } {}
        void disconnect() {
            std::cout << "disconnecting " << id << "\n";
            status = Status07::DISCONNECTED;
        }
        void welcome() {
            std::cout << "[sending] welcome dear client " << id << "\n";
        }
    };

    struct ServerBase07 {
        void on_client(Client07* c) {
            if (c->status == Status07::AUTHORIZED) c->welcome();
            if (c->status == Status07::NOT_AUTHORIZED) c->disconnect();
        }
    };

    struct LoggerPlugin07 {
        void on_client(Client07* c) { std::cout << "client " << c->id << ", status: "  << (int) c->status << "\n"; }
    };

 view all matches for this distribution
 view release on metacpan -  search on metacpan

( run in 1.481 second using v1.00-cache-2.02-grep-82fe00e-cpan-2c419f77a38b )