• calling overload methods from constructors

    From Rainer Weikusat@rweikusat@talktalk.net to comp.lang.perl.misc on Wed Jul 3 21:44:56 2024
    From Newsgroup: comp.lang.perl.misc

    Issue I had to deal with recently: I have a class (traditional Perl-OO,
    ie, a package) representing a layer 4 'flow' (source and destinaton
    address, protocol, source and destination port) which will ultimatively
    be used to block the specified flow on a Niagara packet broker. The class
    ctor does duplicate detection by building a key string from its
    parameters and looking for that in a hash as a second (suricata) alert
    for the same flow shouldn't result in it being blocked twice (harmless,
    but useless).

    I was then asked to add "layer 3 blocking" to this
    particular program, ie, block all communications between the specified
    source and destination IP. This should also do duplicate detection in
    the same way and future requests to block an l4 flow should be considered duplicates if an l3 block for the source and destination addresses
    already exists, in addition to the existing l4 duplicate detection. As
    an l4 block is just an l3 block with protocol and ports added, the l3
    block class should be a parent of the l4 block class as most of the code
    is usable for both cases. After some headscratching, I came up with the following solution:

    The ctor of the l3 block class looks like this:

    sub new
    {
    my $class = shift;
    my ($self, $k, @w);

    $k = $class->new_key_if(\%blks, @_); #1
    return unless defined($k);

    $self = bless([], $class);
    $$self[KEY] = $k;

    @w = $::blk_q->put($self);
    $::poller->add_want($_) for @w;

    return $self;
    }

    The l4 block class just inherits this. The line marked with #1 does a
    call of an overloaded method and passes whatever arguments were passed
    to the ctor for it, ie, this will call the l3 block new_key_if method if
    an l3 block object is being constructed and the l4 block new_key_if
    method for an l4 block object. This method looks like this:

    sub new_key_if
    {
    my ($self, $blks, $src, $dst, $proto, $sport, $dport) = @_;
    my $k;

    $k = $self->SUPER::make_key($src, $dst);
    return if exists($$blks{$k});

    return $self->SUPER::new_key_if($blks, $src, $dst, $proto, $sport, $dport); }

    It invokes the superclass key construction method for l3 duplicate
    detection and then, invokes the superclasss new_key_if with all its
    parameters. This method is

    sub new_key_if
    {
    my ($self, $blks) = (shift, shift);
    my $k;

    $k = $self->make_key(@_);
    return if exists($$blks{$k});

    $$blks{$k} = 1;
    return $k;
    }

    This removes the first two arguments which are the invocant and a
    reference to the duplicate detection hash. In then invokes the make_key
    method with the remaining arguments. Depending on which kind of object
    is being constructed, this will either build a l3 key or a l4 key. Apart
    from that, the code is identical for both classes.

    This means for object construction, I now have 17 lines of code shared
    between both classes and a further 7 for the behaviour that's specific
    to the l4 block class.

    The crucial bits for implementing this are two Perl features:

    1) Ability to call methods possibly overloaded by a subclass without a
    class instance (blessed reference) by using the class name as invocant.

    2) Ability of a subroutine ("method") to call another subroutine with
    the arguments passed to it without knowing what these arguments actually
    are (for the l4 case, 3 additional arguments are being passed the l3
    code being reused for this is oblivious to).
    --- Synchronet 3.20a-Linux NewsLink 1.114