[33010] in Perl-Users-Digest

home help back first fref pref prev next nref lref last post

Perl-Users Digest, Issue: 4286 Volume: 11

daemon@ATHENA.MIT.EDU (Perl-Users Digest)
Thu Sep 18 14:14:22 2014

Date: Thu, 18 Sep 2014 11:14:11 -0700 (PDT)
From: Perl-Users Digest <Perl-Users-Request@ruby.OCE.ORST.EDU>
To: Perl-Users@ruby.OCE.ORST.EDU (Perl-Users Digest)

Perl-Users Digest           Thu, 18 Sep 2014     Volume: 11 Number: 4286

Today's topics:
        Modules, how to store private data <justin.1401@purestblue.com>
    Re: Modules, how to store private data <peter@makholm.net>
    Re: Modules, how to store private data <m@rtij.nl.invlalid>
    Re: Modules, how to store private data <news@lawshouse.org>
    Re: Modules, how to store private data <rweikusat@mobileactivedefense.com>
    Re: Something's wrong here with zenity progress <rweikusat@mobileactivedefense.com>
    Re: Something's wrong here with zenity progress <gamo@telecable.es>
    Re: Something's wrong here with zenity progress <gamo@telecable.es>
    Re: Something's wrong here with zenity progress <rweikusat@mobileactivedefense.com>
    Re: Something's wrong here with zenity progress <rweikusat@mobileactivedefense.com>
        somewhat amusing OO pitfall <rweikusat@mobileactivedefense.com>
    Re: somewhat amusing OO pitfall <kaz@kylheku.com>
    Re: somewhat amusing OO pitfall <rweikusat@mobileactivedefense.com>
    Re: utility perl module <news@lawshouse.org>
        Digest Administrivia (Last modified: 6 Apr 01) (Perl-Users-Digest Admin)

----------------------------------------------------------------------

Date: Thu, 18 Sep 2014 12:50:05 +0100
From: Justin C <justin.1401@purestblue.com>
Subject: Modules, how to store private data
Message-Id: <dgtqeb-7mp.ln1@zem.masonsmusic.co.uk>


I've been writing my modules like this:

--begin--
package My::Module;

use warnings; use strict; use [whatever else I need]

sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
    return $self;
}

 ...later:
sub something {
    my $self = shift;
    if (@_) {
        $self->{something} = shift;
    }
    return $self->{something};
}
--end--


I don't know if this is right or wrong (or indifferent). What this
allows, though, is, AIUI, the person using the module to directly 
access the data without using my methods. I thought about it and 
then tried:


--begin--
package My::Module;

use warnings; use strict; use [whatever else I need]

my %private_data;

sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
    return $self;
}

 ...later:
sub something {
    my $self = shift;
    if (@_) {
        $private_data{something} = shift;
    }
    return $private_data{something};
}
--end--

Now when I examine the object created by My::Module->new() 
I don't see the private data... and apparently things are still
working (not extensively tested, just a couple of modules, but
no errors reported yet).

Are there any reasons I shouldn't adopt this method for my future
module development? Have I been doing it wrong up until now or,
as always with perl it's just that TIMTOWTDI? 

   Justin.

-- 
Justin C, by the sea.


------------------------------

Date: Thu, 18 Sep 2014 14:30:10 +0200
From: Peter Makholm <peter@makholm.net>
Subject: Re: Modules, how to store private data
Message-Id: <87oaudyuul.fsf@vps1.hacking.dk>

Justin C <justin.1401@purestblue.com> writes:

> my %private_data;

[...]

> sub something {
>     my $self = shift;
>     if (@_) {
>         $private_data{something} = shift;
>     }
>     return $private_data{something};
> }

This makes $private_data{something} shared between all instances of your
class. Instead you need to use an identifier unique for your object:

sub something {
    my $self = shift;
    if (@_) {
         $private_data{...}{something} = shift;
    }
    return $private_data{...}{something};
}

So what can be used for identifying the object? Well, as long as the
object lives, the address used by the reference is unique and if you use
the object itself as hash key it will actually us a representation of
the address. So:

      $private_data{$self}{something} = shift;

would work as long as objects stays live. This means you need to clean
up when an object is destroyed. This can be done by adding a destructor
for your object:

sub DESTROY {
    my $self = shift;

    delete $private_data{$self};
}

This is basically how Inside-Out objects works. You can see more
documentation for this idea in the perlobj manual page and by any of the
modules implementing Inside-Out objects, for example Object::InsideOut.

But note that even though you can't see the data by naïve introspection
of the object it self (i.e. using Data::Dumper) data is still available
to an insisting user by way of the PadWalker module.

> Are there any reasons I shouldn't adopt this method for my future
> module development? Have I been doing it wrong up until now or,
> as always with perl it's just that TIMTOWTDI? 

Inside-Out objects was very much hyped a few years ago. These days I
don't see as many caring about the features provided by this technology
and then it is simpler to just use ordinary old style perl objects.

My solution is just to beat users that knowlingly breaks the
encapsulation with a used tea bag. That works too.

//Makholm


------------------------------

Date: Thu, 18 Sep 2014 14:35:59 +0200
From: Martijn Lievaart <m@rtij.nl.invlalid>
Subject: Re: Modules, how to store private data
Message-Id: <f60reb-235.ln1@news.rtij.nl>

On Thu, 18 Sep 2014 12:50:05 +0100, Justin C wrote:

> Are there any reasons I shouldn't adopt this method for my future module
> development? Have I been doing it wrong up until now or, as always with
> perl it's just that TIMTOWTDI?

You are doing it wrong now, as your new approach will share the data over 
all objects.

But yes, TIMTOWTDI, and what you are trying to do is already done by 
others. The best known implementation is Moose.

M4


------------------------------

Date: Thu, 18 Sep 2014 14:28:30 +0100
From: Henry Law <news@lawshouse.org>
Subject: Re: Modules, how to store private data
Message-Id: <-aWdnWQnB69jQIfJnZ2dnUVZ8lidnZ2d@giganews.com>

On 18/09/14 12:50, Justin C wrote:
> What this
> allows, though, is, AIUI, the person using the module to directly
> access the data without using my methods.

Indeed; that's often the case.  Just warn people loudly not to do that, 
and change the internal structure (so their direct-access code breaks) 
every time you release a new version; they'll soon learn.

-- 

Henry Law            Manchester, England


------------------------------

Date: Thu, 18 Sep 2014 14:53:45 +0100
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: Modules, how to store private data
Message-Id: <871tr9591y.fsf@sable.mobileactivedefense.com>

Justin C <justin.1401@purestblue.com> writes:
> I've been writing my modules like this:
>
> --begin--
> package My::Module;
>
> use warnings; use strict; use [whatever else I need]
>
> sub new {
>     my $class = shift;
>     my $self = {};
>     bless $self, $class;
>     return $self;
> }
>
> ...later:
> sub something {
>     my $self = shift;
>     if (@_) {
>         $self->{something} = shift;
>     }
>     return $self->{something};
> }
> --end--
>
>
> I don't know if this is right or wrong (or indifferent). What this
> allows, though, is, AIUI, the person using the module to directly 
> access the data without using my methods. I thought about it and 
> then tried:

"Perl doesn't have an infatuation with enforced privacy.  It would
 prefer that you stayed out of its living room because you weren't
 invited, not because it has a shotgun."

> --begin--
> package My::Module;
>
> use warnings; use strict; use [whatever else I need]
>
> my %private_data;
>
> sub new {
>     my $class = shift;
>     my $self = {};
>     bless $self, $class;
>     return $self;
> }
>
> ...later:
> sub something {
>     my $self = shift;
>     if (@_) {
>         $private_data{something} = shift;
>     }
>     return $private_data{something};
> }
> --end--
>
> Now when I examine the object created by My::Module->new() 
> I don't see the private data... and apparently things are still
> working (not extensively tested, just a couple of modules, but
> no errors reported yet).
>
> Are there any reasons I shouldn't adopt this method for my future
> module development?

Unless there's some specific other reason why storing object instance
data in this way makes sense[*], it just makes debugging (and coding!)
more complicated (and performance worse) without providing anything in
return.

[*] Real-world example where I use this: In a certain program, there's a
kind of class called 'a link' which represents a connection to an object
stored in a certain database object. For reasons I'm going to omit
here, the referred to object is represented as pair (<object id>,
<database name>). Databases have to be serialized into text files and
restored from them and they hold/ manage complex objects which can
contain any number of such links to any kind of other objects. The main
purpose of a Link object is that its owner can use it to register a
trigger routine which is called automatically when the state of the
referred-to object changes. The typical use is that an individual owner
uses the same trigger routine for all links owned by it (detail
information about the change is provided in form of arguments to the
trigger subroutine) and there can be a lot of these links (more than
500,000 on large installations). Also, serializing the trigger-related
information makes no sense because it will differ between different
program runs. A trigger record is represented as triple

(<coderef>, <weakref to owner>, <refcount>)

so that many Link objects can share a trigger record. Objects and
trigger records are associated by using a hash (the explicit refcount is
needed because trigger records are also stored in a 2nd hash using owner
and coderef as key so that they can be found in case a new Link objects
needs an already created trigger record).


------------------------------

Date: Mon, 15 Sep 2014 12:21:33 +0100
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: Something's wrong here with zenity progress
Message-Id: <87oauh5dtu.fsf@sable.mobileactivedefense.com>

gamo <gamo@telecable.es> writes:
> 'zenity' is a piece of comand line utility to provide GUI (Gtk)
> for the basic input or static output operations on a script.

[...]

> $|=1;

This configures the currently selected output filehandle for 'autoflush
mode'. That's likely going to be STDOUT ...

> my @list = 0..10; # warning, do not increase!
> progress ( "Se ha completado el 100 %", "# Progreso:" , \@list );
>
>
> sub progress {
>     my ($title, $text, $percentage) = @_;
>     open ZEN, "|-", "zenity --progress title=\"$title\" text=\"$text\"
> percentage=0" or
>       die "Cannot open zen";

 ... and certainly not this filehandle. 

>     for my $i (@$percentage) {
>         print ZEN "$i\n";
>         if ( $? == -1 ) {
>             error( "¡Cancelado!" );
>             exit -1;
>         }
>         sleep 1;
>     }

Consequently, your progress display won't update timely because ZEN is
fully buffered. You'll need to change the buffering mode of the ZEN file
handle. This can be accomplished by 'shooting arrows',

ZEN->autoflush(1);

or by using the select function to change the selected filehandle, eg

select((select(ZEN), $| = 1)[0]);

select returns the previously selected file handle. This expression
changes the currently selected filehandle to ZEN, sets autoflush for it
and then changes the selected filehandle back to what it was before the
inner select (by calling select again with the first member of the list

(select(ZEN), $| = 1)

as argument which is the previously selected file handle.

Working example:

------------
#!/usr/bin/perl

use POSIX qw(_exit);

my ($fh, $pid, $nr, $buf);

$pid = open($fh, '|-');
$pid // die("$!");

if ($pid == 0) { 
    print STDERR ($buf) while  sysread(STDIN, $buf, 1024) > 0;
    _exit(0);
}

select((select($fh), $|=1)[0]);

print $fh ("abc\n");
sleep(1);
print $fh ("abc\n");
sleep(1);
print $fh ("abc\n");
sleep(1);
print $fh ("abc\n");
------------

NB: In case you want to do more elaborate error handling if creation of
the child process failed, it's not safe to use open with -| or |- to
create it because contrary to the documentation, it will execute (at
least in some version) an equivalent of

die("Can't fork")

if the implicit fork failed.


------------------------------

Date: Mon, 15 Sep 2014 13:53:51 +0200
From: gamo <gamo@telecable.es>
Subject: Re: Something's wrong here with zenity progress
Message-Id: <lv6k0u$qbu$1@speranza.aioe.org>

El 15/09/14 a las 13:21, Rainer Weikusat escribió:
> gamo <gamo@telecable.es> writes:
>> 'zenity' is a piece of comand line utility to provide GUI (Gtk)
>> for the basic input or static output operations on a script.
>
> [...]
>
>> $|=1;
>
> This configures the currently selected output filehandle for 'autoflush
> mode'. That's likely going to be STDOUT ...
>
>> my @list = 0..10; # warning, do not increase!
>> progress ( "Se ha completado el 100 %", "# Progreso:" , \@list );
>>
>>
>> sub progress {
>>      my ($title, $text, $percentage) = @_;
>>      open ZEN, "|-", "zenity --progress title=\"$title\" text=\"$text\"
>> percentage=0" or
>>        die "Cannot open zen";
>
> ... and certainly not this filehandle.
>
>>      for my $i (@$percentage) {
>>          print ZEN "$i\n";
>>          if ( $? == -1 ) {
>>              error( "¡Cancelado!" );
>>              exit -1;
>>          }
>>          sleep 1;
>>      }
>
> Consequently, your progress display won't update timely because ZEN is
> fully buffered. You'll need to change the buffering mode of the ZEN file
> handle. This can be accomplished by 'shooting arrows',
>
> ZEN->autoflush(1);
>
> or by using the select function to change the selected filehandle, eg
>
> select((select(ZEN), $| = 1)[0]);
>
> select returns the previously selected file handle. This expression
> changes the currently selected filehandle to ZEN, sets autoflush for it
> and then changes the selected filehandle back to what it was before the
> inner select (by calling select again with the first member of the list
>
> (select(ZEN), $| = 1)
>
> as argument which is the previously selected file handle.
>
> Working example:
>
> ------------
> #!/usr/bin/perl
>
> use POSIX qw(_exit);
>
> my ($fh, $pid, $nr, $buf);
>
> $pid = open($fh, '|-');
> $pid // die("$!");
>
> if ($pid == 0) {
>      print STDERR ($buf) while  sysread(STDIN, $buf, 1024) > 0;
>      _exit(0);
> }
>
> select((select($fh), $|=1)[0]);
>
> print $fh ("abc\n");
> sleep(1);
> print $fh ("abc\n");
> sleep(1);
> print $fh ("abc\n");
> sleep(1);
> print $fh ("abc\n");
> ------------
>
> NB: In case you want to do more elaborate error handling if creation of
> the child process failed, it's not safe to use open with -| or |- to
> create it because contrary to the documentation, it will execute (at
> least in some version) an equivalent of
>
> die("Can't fork")
>
> if the implicit fork failed.
>

Thank you for the detailed explanation. I understand that if I change
the filehandle from ZEN to STDOUT the problem is solved. The question
is if it solves too the $? issue or I need to use -|- if it's
permitted.

Thanks

-- 
http://www.telecable.es/personales/gamo/


------------------------------

Date: Mon, 15 Sep 2014 14:46:02 +0200
From: gamo <gamo@telecable.es>
Subject: Re: Something's wrong here with zenity progress
Message-Id: <lv6n2p$2q0$1@speranza.aioe.org>

El 15/09/14 a las 13:53, gamo escribió:

>
> Thank you for the detailed explanation. I understand that if I change
> the filehandle from ZEN to STDOUT the problem is solved. The question
> is if it solves too the $? issue or I need to use -|- if it's
> permitted.
>
> Thanks
>

No, better stay with ZEN, but the problems seems solved using the
select((select(ZEN),$|=1)[0]);
trick.

Thank you very much.

PD: -|- seem accepted by perl -c but after running prints a warning
-- 
http://www.telecable.es/personales/gamo/


------------------------------

Date: Mon, 15 Sep 2014 15:04:47 +0100
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: Something's wrong here with zenity progress
Message-Id: <87a961m134.fsf@sable.mobileactivedefense.com>

gamo <gamo@telecable.es> writes:

[...]


> sub progress {
>     my ($title, $text, $percentage) = @_;
>     open ZEN, "|-", "zenity --progress title=\"$title\" text=\"$text\"
> percentage=0" or
>       die "Cannot open zen";
>     for my $i (@$percentage) {
>         print ZEN "$i\n";
>         if ( $? == -1 ) {
>             error( "¡Cancelado!" );
>             exit -1;
>         }

And this doesn't seem to make much sense: In case the child process
exits, the pipe will become severed and the parent will receive a
SIGPIPE whose default action is to terminate the process. In case it's
either ignored or handled, the print will fail with an EPIPE error. In
any case, $? won't have a value until the parent process waited for the
desceased child.

---------------
my ($fh, $pid);

$pid = open($fh, '|-', 'pause');

$SIG{PIPE} = 'IGNORE';
kill(15, $pid);

sleep(1); # give the child some time to die

syswrite($fh, 'bla') or print STDERR ("$!\n");

print STDERR ("$?\n");

wait();

print STDERR ("$?\n");


------------------------------

Date: Mon, 15 Sep 2014 15:08:53 +0100
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: Something's wrong here with zenity progress
Message-Id: <8761gpm0wa.fsf@sable.mobileactivedefense.com>

Rainer Weikusat <rweikusat@mobileactivedefense.com> writes:

> $pid = open($fh, '|-', 'pause');

I forgot that pause is not a standard tool. The source code for this
command (likely of little commercial value :-) is

---------
/*
  do a pause call

  	$Id: pause.c,v 1.2 2014/09/15 14:07:35 rw Exp $
*/

/*  includes */
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>

/*  main */
int main(int argc, char **unused)
{
    (void)unused;

    if (argc != 1) {
	openlog("pause", LOG_PID | LOG_PERROR, LOG_USER);
	syslog(LOG_NOTICE, "Usage: pause");

	exit(1);
    }

    pause();
    return 0;
}


------------------------------

Date: Thu, 18 Sep 2014 18:36:14 +0100
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: somewhat amusing OO pitfall
Message-Id: <87ha04u8z5.fsf@sable.mobileactivedefense.com>

This is a paraphrase of some real code I wanted to use in 'some
context'. The idea was that all arguments passed to the top-level
function should be passed on as is, ie, without copying the values, just
the first one needed to be replaced:

---------------
package Suicidal;

sub print_it
{
    print @_, "\n";
}

sub new
{
    return bless([], $_[0]);
}

sub DESTROY
{
    print STDERR ("Argh. Someone overwrote me.\n");
}

sub do_print
{
    $_[0] = 'He said: ';
    &print_it;
}

package main;

my $self = Suicidal->new();

$self->do_print('I\'m the silent killer!');
--------------

Since

$self->do_print(...)

ends up doing exactly the same as

Suicidal::do_print($self, ...)

the assignment in do_print overwrites the only existing reference to the
object.



------------------------------

Date: Thu, 18 Sep 2014 17:41:37 +0000 (UTC)
From: Kaz Kylheku <kaz@kylheku.com>
Subject: Re: somewhat amusing OO pitfall
Message-Id: <20140918103948.733@kylheku.com>

On 2014-09-18, Rainer Weikusat <rweikusat@mobileactivedefense.com> wrote:
> the assignment in do_print overwrites the only existing reference to the
> object.

I don't see how this warrants the subject line; can you translate
translate this pitfall to two or three decent OO languages, with
its key nuances intact?


------------------------------

Date: Thu, 18 Sep 2014 19:03:43 +0100
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: somewhat amusing OO pitfall
Message-Id: <87d2asu7pc.fsf@sable.mobileactivedefense.com>

Kaz Kylheku <kaz@kylheku.com> writes:
> On 2014-09-18, Rainer Weikusat <rweikusat@mobileactivedefense.com> wrote:
>> the assignment in do_print overwrites the only existing reference to the
>> object.
>
> I don't see how this warrants the subject line;

It happens because of the way subroutines in Perl work and the way
method invocations are mapped to them. The actual effects were a little
bit more interesting because the destructor unmapped an mmapped file
area and destroyed the file and the ultimate outcome was a segfault in a
C routine supposed to write data to this file.

> can you translate translate this pitfall to two or three decent OO
> languages, with its key nuances intact?

Provided they're all devent enough to work like Perl, probably.


------------------------------

Date: Mon, 15 Sep 2014 21:27:42 +0100
From: Henry Law <news@lawshouse.org>
Subject: Re: utility perl module
Message-Id: <E9ydnS6mbsYj1orJnZ2dnUVZ7tOdnZ2d@giganews.com>

On 09/09/14 20:20, herbert.burnswell@gmail.com wrote:
> It works fine when called from other scripts however, as you can see, I have to define $logfile in the PM to get it to work.  I'd like to define the $logfile in the scripts that call these routines.

I sense you are following the same path I've been on while improving my 
Perl over the last few years.  If so then let me encourage you, and 
offer you some suggestions for your next step in perfecting this module.

You've made it a package; why not try making it object-oriented?  That 
way you get a Log object of your own making, with properties 
(particularly the log file name, but also other things you may find 
useful as you develop).  Start with how you'd use your module; something 
like this, maybe:

   use My::Logger;
   my $thislog = My::Logger->new( '/the/first/log/file' )
     or die "Log file creation failed";
   $thislog->open('this is the log header');
   $thislog->write( 'some stuff' );
   # or maybe
   $thislog->write( 'a line', 'another line' );
   # You might like to allow logs to overwrite or append
   my $another_log = My::Logger->new( '/some/other/log/file',
                     {append=>1} );
   $another_log->open("This is a second log, with lines appended");
   # Then maybe you might find the need to code a method for the number
   # of lines written
   print $thislog->lines_written(), " lines written to log";
   # Your log object can implement its own error reporting
   $thislog->write( 'foo ') or die "Log error:".$thislog->errstr();
   # You can do really far out stuff like this:
   my @logs;
   for ( qw( hourly daily weekly monthly ) ) {
     push @logs, My::Logger->new( "/var/log/somewhere/$_" )
       or die "Couldn't open '$_' log: $!";
   }

 ... and so on.  Fine if you don't want to do that; what you've got 
works, with the amendments that the other posters (much more 
knowledgeable than I) have made.

-- 

Henry Law            Manchester, England


------------------------------

Date: 6 Apr 2001 21:33:47 GMT (Last modified)
From: Perl-Users-Request@ruby.oce.orst.edu (Perl-Users-Digest Admin) 
Subject: Digest Administrivia (Last modified: 6 Apr 01)
Message-Id: <null>


Administrivia:

To submit articles to comp.lang.perl.announce, send your article to
clpa@perl.com.

Back issues are available via anonymous ftp from
ftp://cil-www.oce.orst.edu/pub/perl/old-digests. 

#For other requests pertaining to the digest, send mail to
#perl-users-request@ruby.oce.orst.edu. Do not waste your time or mine
#sending perl questions to the -request address, I don't have time to
#answer them even if I did know the answer.


------------------------------
End of Perl-Users Digest V11 Issue 4286
***************************************


home help back first fref pref prev next nref lref last post