Beefy Boxes and Bandwidth Generously Provided by pair Networks RobOMonk
go ahead... be a heretic
 
PerlMonks  

Adding autoloaded methods to symbol table with using strict refs

by strat (Canon)
on Feb 25, 2002 at 13:19 UTC ( #147326=perlquestion: print w/ replies, xml ) Need Help??
strat has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,
I've got the following problem: I want to build generic object interfaces with autoload, and create an entry in the symbol-table with the first emulation. This works fine, but I need to turn off strict refs, als shown in the following code example:
#!perl use warnings; use strict; my $test = Test->new(); $test->print("hello\n"); $test->print("hello2\n"); $test->print("hello3", "hello4\n"); # ============================================================ package Test; # ------------------------------------------------------------ sub new { # any constructor my ($proto) = shift; my $class = ref($proto) || $proto; my $parent = ref($proto) && $proto; my $self; $self = {}; # create a new object bless($self, $class); } # new # ------------------------------------------------------------ sub AUTOLOAD { use vars qw($AUTOLOAD); my ($self, @params) = @_; my ($method) = (split(/::/, $AUTOLOAD))[-1]; print "AUTOLOAD: $AUTOLOAD\nSELF: $self\n"; print "METHOD: $method\nPARAMS: @params\n"; # create code my $code = sub { my ($self, @params) = @_; print @params, "\n"; }; $self->$code(@params); # execute for the first time # and add it to symbol table no strict 'refs'; *{$method} = $code; use strict 'refs'; } # AUTOLOAD # ------------------------------------------------------------ sub DESTROY { # don't autoload DESTROY :-) # ... } # DESTROY # ------------------------------------------------------------
Do you know how am I able to do so without turning off strict refs?

Thanks in advance,

perl -le "s==*F=e=>y~\*martinF~stronat~=>s~[^\w]~~g=>chop,print"

Comment on Adding autoloaded methods to symbol table with using strict refs
Download Code
Re: Adding autoloaded methods to symbol table with using strict refs
by lachoy (Parson) on Feb 25, 2002 at 13:49 UTC

    I don't think you can do that. But IMO you don't need to worry about it -- turning off a feature like this is fine, as long as you know what you're doing and the effects are limited.

    Note that you don't need the use strict 'refs' after you assign the sub, since the no strict 'refs' only applies until the block ends. This is why many times you'll see something like:

    ...normal code here... { no strict 'refs'; ... do fun things with the symbol table ... } ...continue normal code...

    So the effects of no strict 'refs' are limited to its immediate block.

    Chris
    M-x auto-bs-mode

Re: Adding autoloaded methods to symbol table with using strict refs
by chromatic (Archbishop) on Feb 25, 2002 at 20:59 UTC
(Ovid - minor code nits) Re: Adding autoloaded methods to symbol table with using strict refs
by Ovid (Cardinal) on Feb 25, 2002 at 21:23 UTC

    This is one of those times where it's appropriate to disable strict refs. However, there are a couple of issues that you may wish to be aware of. First, I see a use vars statement right after the opening brace of the AUTOLOAD sub. If you're just putting it there because this is conceptually where you feel it should go, that's probably okay, even though I confess that I prefer my use statements grouped together at the beginning of a program/module. However, I see that many programmers put the use statement in a subroutine thinking that it will delay the use of the module or pragma until needed. If that's what you are doing, you should be aware that use happens at compile time, not run time. Unfortunately, you can't just require in the vars pragma and then pass in an import list as this will kill your program under strict. If you are using 5.6 or later, you can do this:

    sub AUTOLOAD { our $AUTOLOAD; ... }

    If you're using a prior version, you may as well slap a 'no strict' at the top of the subroutine (yuck). Otherwise, if you expect to be calling AUTOLOAD virtually every run of the program, I'd put the use statement at the top of the package.

    No offense, but do you know what the following does?

    sub new { # any constructor my ($proto) = shift; my $class = ref($proto) || $proto; my $parent = ref($proto) && $proto; my $self; $self = {}; # create a new object bless($self, $class); } # new

    You have created a scalar named $parent, but you don't use it. Further, you're assigning to $class with ref($proto) || $proto. What is your reason for doing the latter? This is typically used if you want to clone an object. This might be appropriate, but rarely is it. I would change your constructor to the following, unless you have a decent justification otherwise:

    sub new { # any constructor my $class = shift; my $self = {}; # or some initializing routine bless $self, $class; } # new

    Once you get everything working with that, it should be fine. If you need the other functionality later, you can add it in as needed.

    Hope this helps.

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Thank you very much for your help and especially thx to Ovid.

      I put the use vars in at that position - in this special case - I feel that this is the position it belongs to.

      Unfortunately, I can't use perl5.6x yet, because it is too slow and takes too much memory
      Perl5.005_03/AS 522: ~20 minutes, ~450 MB RAM
      Perl5.61/AS 631: ~90 minutes, ~700 MB RAM

      Please, don't wonder about this huge memory usage; this script synchronizes some Sql-databases with a Directory System from a big german bank, and I chose to prefer a shorter runtime (which was about 34 hours before I started this project) to memory usage.

      The posted Script is just a little Testscript because I didn't want to post the original code, for it is much too long. I think, this code shows the issues, and as my english is not so good, it might have helped me to say what i wanted to do :-)

      Ad constructor: in 90%, it's just a "normal" constructor, but sometimes I need to "clone" objects, so that's what my $class = ref($proto) || $proto; stands for. The $parent isn't needed any more, I played around some months ago with it, and somehow it has survived.

      Best regards,
      perl -le "s==*F=e=>y~\*martinF~stronat~=>s~[^\w]~~g=>chop,print"

      I started writing a text explaining why I like $class = ref $proto || $proto; but everything felt unnecessary to write, since everything seemed so obvious. So instead of explaining why I think it's proper and why I like it I'll flip the coin and ask why you think it's mostly inappropriate.

      -Anomo
        Actually, I don't think it's nummy, but that's neither here nor there.

        There is good reasoning behind doing my $class = ref $proto || $proto;. Unfortunately, 99% of people who do that have no idea why they're doing it. The smarter ones will realize that it's doing some sort of error-checking and chalk it up to that.

        What that's really doing is co-opting your constructor (typically called new()) and using it as a copy function, (typically called copy() or clone()). There is something in C++ (and, I believe, Java) called a copy constructor, which will do that cloning for you.

        I personally think that new() should be reserved for creating a new instance and clone() should be reserved for taking an existing instance and returning a copy of it. That is because the two functionalities are vastly different. new() almost never has to deal with recursive data structures, deep vs. shallow copying, and the like.

        In addition, EVERY single time I've seen ref $proto || $proto written, there was no provision made for a copy constructor. In fact, that was the last it did with $proto. Doesn't seem like much of a copy constructor to me!

        Personally, I prefer the following construction:

        sub new { my $class = shift; return undef if ref $class; .... }
        That makes it completely obvious that I'm using new() to return a new instance. I'll create a copier if (and only if) I need one.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        That construct allows you to call new() on an already-constructed object, receiving back a new object. This would be handy in certain circumstances as a copy constructor.

        The problem is, there's no copying going on in the constructor. It creates a new, empty hash. That's not so useful.

        The only other reason something like this would be useful is if you want to create an object of the same type without knowing what that type is. I can't think of when that would be useful, and there are other ways to solve this, but I'd allow it in this case.

        Otherwise, it adds nothing. (Some people would argue that a copy constructor should have a different name, like "clone" or such. I lean toward the idea that methods should each do one thing well.)

        One of the strengths/weaknesses of Perl's OO system is the lack of a clean separation of class and instance data and methods. This typically isn't that big of a deal, but many Perl programmers have never used OO programming with another language and may not appreciate the distinction between the two.

        Imagine that you are writing some sort of bank account software. In your specs, you see that if anyone has a minimum savings balance less than $100US, you stop paying interest on that account. Since this applies to all accounts, the limit of 100 dollars is class data. It doesn't apply to an individual account so much as it applies to all accounts. If you need to change the limit, you can adjust the class variable and all objects automatically pick up the new value (yes, this is a simplistic example). However, if you want to check my savings balance, you would check the instance value of my account's balance. Obviously, having my account balance be class data would not make sense.

        The separation between instance and class also affects methods. The typical new() constructor is a class method. In this case, it usually doesn't impart any new information to specific instances of the object being instantiated (it might do this, though, if you were keeping track of the number of accounts). It doesn't, however, typically belong to a particular instance of an object. That usually doesn't make a sensible model.

        The ref $proto || $proto construct is saying "I might call this as a class method" (a constructor of a brand new object) or "I might call this as an instance method" creating a copy or clone of an object. Those two ways of calling the method seem related, but conceptually they usually are not. If you need an instance method to create a clone, then write an instance method rather than trying to override the behavior of the constructor.

        You also have to consider that other programmers may have to maintain your code. Remember that OO programming, at it's heart, is to simulate a system (the first OO language was even called "Simula"). As such, it really should model something in an intuitive fashion. That is, in my opinion, yet another reason not to confuse class and instance methods. It's relatively little work to separate those out and make a cleaner interface.

        Hope that helps.

        Cheers,
        Ovid

        Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://147326]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (19)
As of 2014-04-16 14:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (427 votes), past polls