Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm using the XMLRPC Frontier::Daemon module which uses IO::Socket as one of it's base classes. My problem is that when I create a method for the Daemon, the sub does not appear to have an object reference (commonly set to $self or $this) as the first parameter passed. I do pass 2 paramters and they are the only ones being received by the sub. Don't ALL object methods have the object reference passed as the first parameter?

I need to acquire the $self so that I can access one of the base class methods ( connected() which is in IO::Socket ).

Strictly speaking Frontier::Daemon is a subclass of HTTP::Daemon, which is a subclass of IO::Socket::INET which is built upon IO::Socket. Sample code:

use Frontier::Daemon my $id = Frontier::Daemon->new( methods => { sum => \&sum, }, LocalAddr => '127.0.0.1', LocalPort => 80, Reuse => 1, ) or die ("Cannot start RCP daemon: $!"); sub sum { my $self = shift; # This is getting arg1 not the object reference! my $arg1 = shift; # This is getting arg2. my $arg2 = shift; ... }

Everything I read about object methods indicates that all methods should always receive the object reference as the first parameter. (Which is even discarded in most cases because it is not really needed.) But the CPAN documentation for the Frontier::Daemon (with accompanying examples) neglect to extract the reference in the object methods. Why is this server class object different from all others?

The server itself is working fine and does everything it is supposed to. I just need to give it some extra functionality and for that I need access to some class methods.

Not that it should make a difference, but the call to the Daemon constructor does not return (as it was designed this way by the module designer.)

thanks..

Replies are listed 'Best First'.
Re: Missing object reference in sub
by chromatic (Archbishop) on Sep 26, 2007 at 18:37 UTC

    Perl 5 doesn't make a strong distinction between subroutines and methods. The magic is really at the point of invocation, and that's whether you call the function as a method on an invocant or invoke it directly.

    I don't know the particulars of this API, but if the methods are supposed to be callbacks, you may have to create a thunking closure that invokes the method on the object appropriately:

    sum => sub { $obj->sum( @_ ) },

      To quote the documentation:

      Frontier::Daemon takes a `methods' parameter, a hash that maps an incoming RPC method name to reference to a subroutine.

      The Client side code for the example is

      use Frontier::Client; my $url = "http://127.0.0.1/RPC2"; # Server my @args = (2,3); # values to pass to sum() # Create the client my $client = Frontier::Client->new( url => $url, debug => 0, ); print "$args[0] + $args[1] = ", $client->call('sum', @args), "\n";

      So the client doing the calling can't pass the object refefence.

      I can't do what chromatic said because I do not have an $obj at the point. (Remember that the call to the Daemon constructor never returns.)

      I tried looking at the module source myself, but they do some weird stuff in there. Beyond my limited scope. (And they call it 'weird' themselves so they know it.) I'd post some of it here but it's on an innaccessble machine at the moment.

      I didn't expect anyone to be familiar with the Frontier::Daemon, but I was hoping that someone who had used the HTTP::Daemon ran into the same situation as myself. They would have ran into the same problem as me.

        I can't do what chromatic said because I do not have an $obj at the point. (Remember that the call to the Daemon constructor never returns.)

        Sure you can. The closure has to bind to the lexical at compile time, but the object has to be valid at the point of the call, which is after the object creation. Add one line:

        my $id; $id = Frontier::Daemon->new( ... );

        ... then use $id in the closure.

        I tried looking at the module source myself ...

        In case you don't mind modifying the sources... this is what a quick grep turned up (in RPC2.pm / sub serve {...}):

        my $eval = eval { $result = &{ $methods->{$method} }(@{ $call->{'v +alue'} }) }; if ($@) { ... }

        Presuming that the $self available within that serve method is what you need (not sure... but why not try?), you could add it to the argument list yourself, i.e.

        my $eval = eval { $result = &{ $methods->{$method} }($self, @{ $ca +ll->{'value'} }) }; ^^^^^

        Just an idea... (untested)

        I'm confused. At one point you say the constructor never returns, but the example shows using it in assignment and in code afterward. For my possibly helpful reply, I'm assuming that it does return.

        package Frontier::NotReally; sub new { my $class = shift; my %p = @_; return bless \%p, $class; } sub id { my $self = shift; return "$self"; } sub call { my $self = shift; return $self->{sub}->( @_ ); } package main; my $id; $id = Frontier::NotReally->new( sub => sub { print "insub: ", $id->id( +), "\n" } ); print "outsub: ", $id->id(), "\n"; $id->call( 'sub' ); __END__ outsub: Frontier::NotReally=HASH(0x6f1940) insub: Frontier::NotReally=HASH(0x6f1940)

        I don't know how much my mockup, Frontier::NotReally, matches the actual Frontier::Daemon you're dealing with, but it's close enough by my understanding. The important part of this example is that my lexical declaration of $id comes before its actual assignment.

        my $id; # now $id exists! $id = F::D->new( foo => sub { 'here, $id exists too' } );
        What happens is that at the time you call the constructor, $id is undef. Once the constructor finishes, $id has the object's reference in it, and the anonymous sub you passed into the constructor can use it. This won't work if the constructor itself is calling your callback, but any time after that, you'll have the object ready to work with.

        Update: I see chromatic has explained faster than I have.

Re: Missing object reference in sub
by naikonta (Curate) on Sep 26, 2007 at 18:48 UTC
    According to the docs (perlobj), there are three basic rules in Perl OO:
    • An object is simply a reference that happens to know which class it belongs to.
    • A class is simply a package that happens to provide methods to deal with object references.
    • A method is simply a subroutine that expects an object reference (or a package name, for class methods) as the first argument.
    The rule number 3 dictates that a method can be an object method when it expects an object reference as the first argument (called as object method), and can be a class method when it expects a class or package name as its first argument (called as class method). There is, however, a technique to make the method handles them both, depending on how it was called. So, to make your methods take the first argument as object reference, you have to pass it in the object. The way it's done in Perl is using the arrow operator.
    my $obj = Class->new; $obj->a_method($param1, $param2..., $paramN);
    The $obj before the arrow automatically becomes the first argument for a_method. I don't know how the sum() method is called from Frontier::Daemon. But you specify the sum parameter to have value of CODE reference to the sum() method. Also, I don't know in which context the method is defined. If it's merely a callback, you don't have to make an object method.

    But let's assume that sum() is defined in a class called My::Daemon. Then you need to provide the callback in certain way so the sum() method is called with the My::Daemon object.

    package My::Daemon; use strict; use warnings; use Frontier::Daemon; sub new { my($class, @args) = @_; .... my $self = {}; # empty anonymous HASH ref bless $self, $class; # make it an object and return it } sub some_method { my $self = shift; my $id = Frontier::Daemon->new( methods => { sum => sub { # anonymous sub as callback my @args = @_; # @args received from F::D somewhere .... $self->sum(@args); # call sum as object method }, }, ... other params ); } sub sum { my($self, $arg1, $arg2) = @_; ... } 1;
    Now, the $self variable contains the object reference of My::Daemon class.The $arg1 and $arg2 are from @args passed in from the anonymous subroutine.

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!