Thread

Posted on Fri Dec 8 15:07:36 2006 by gaetan
OIO and default value
Hello all,

I have the following class hierarchy:

package Connection; use Object::InsideOut; my @bidule :Arg(name => "bidule", default => 0) :Set(name => "set_bidule", restricted => 1) :Field ; sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]"; } package Connection::Simulate; use Object::InsideOut('Connection');
and the following program
package main; my $sim = Connection::Simulate->new(); $sim->print_information(); 1;

I would like 'bidule' to be set to 1 for objects 'Connection::Simulate' (I already sent a request to have the possibility to set default value without having to use the :Arg attribute. See http://www.cpanforum.com/posts/3633, but the proposed solution does not fit my current pb)

I tried to solve my pb without finding a solution.

For example, I declared a Preinit function in the Connection::Simulate, but the value '1' is not set correctly (this solution is in anyway incorrect: suppose we have the Connection, Connection::Simulate and Connection::Simulate::Blabla classes with default values set to 0 (for Connection), 1 (for Connection::Simulate), 2 (for Connection::Simulate::Blabla))

package Connection::Simulate; use Object::InsideOut('Connection'); sub _preinit :Preinit { my $obj = shift; $obj->set_bidule(1); }

How can I solve my problem (without having to call the set_bidule method outside from the tree hierarchy ;-)) ?

Thanks

Gaetan

Direct Responses: 3725 | Write a response
Posted on Fri Dec 8 15:52:55 2006 by jdhedden in response to 3724
Re: OIO and default value
The :PreInit subroutine should not try to set data in its class's fields or in other class's fields (e.g., using 'set' methods) as such changes will be overwritten during initialization phase which follows preinitialization. The :PreInit subroutine is only intended for modifying initialization parameters prior to initialization.

(I will add the above to the POD for the next release.)

Here's the code that does what you want:
#!/usr/bin/perl use strict; use warnings; package Connection; { use Object::InsideOut; my @bidule :Field :Arg(name => 'bidule', default => 0) :Set(name => 'set_bidule', restricted => 1); sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]\n"; } } package Connection::Simulate; { use Object::InsideOut('Connection'); sub pre :PreInit { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 1; } } } package main; my $sim = Connection::Simulate->new(); $sim->print_information(); print("Done\n"); exit(0); # EOF
Direct Responses: 3727 | Write a response
Posted on Fri Dec 8 16:22:06 2006 by gaetan in response to 3725
Re: OIO and default value
Great !

It works

But my next question is ;-)

How to initialize bidule in Connection::Simulate without having to declare @bidule (in Connection) with the :Arg attribute (I don't want this attribute to be accessible from outside my classes) ?

We could imagine for example the following syntax (in Connection package)

:Arg(name => 'bidule', default => 0, Restricted => 1)

This would solve my problem but this solution, from my point of view, is not an elegant solution

Have a nice we

Gaetan
Direct Responses: 3728 | Write a response
Posted on Fri Dec 8 16:27:05 2006 by jdhedden in response to 3727
Re: OIO and default value
You'd do it with an :Init subroutine in 'Connection':
#!/usr/bin/perl use strict; use warnings; package Connection; { use Object::InsideOut; my @bidule :Field :Set(name => 'set_bidule', restricted => 1); sub init :Init { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 0; } } sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]\n"; } } package Connection::Simulate; { use Object::InsideOut('Connection'); sub pre :PreInit { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 1; } } } package main; my $sim = Connection::Simulate->new(); $sim->print_information(); print("Done\n"); exit(0); # EOF
Direct Responses: 3732 | Write a response
Posted on Fri Dec 8 17:25:09 2006 by gaetan in response to 3728
Re: OIO and default value
It does not work :-(

I obtain the following result:

{go}: perl ~/tmp/toto.txt Use of uninitialized value in concatenation (.) or string at /users/go/tmp/toto.txt line 26. Bidule: Done

Connection::Simulate::pre function is called but with no effect on the @bidule variable AND Connection::Init is not called (no :InitArgs variable is declared ?)

Gaetan
Direct Responses: 3734 | Write a response
Posted on Fri Dec 8 18:30:59 2006 by jdhedden in response to 3732
Re: OIO and default value
Doh! I forgot that after taking out the :Arg attribute, I needed to store the data directly. Sorry.
#!/usr/bin/perl use strict; use warnings; package Connection; { use Object::InsideOut; my @bidule :Field :Set(name => 'set_bidule', restricted => 1); sub init :Init { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 0; } # Store value $self->set(\@bidule, $args->{'bidule'}); } sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]\n"; } } package Connection::Simulate; { use Object::InsideOut('Connection'); sub pre :PreInit { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 1; } } } package main; my $sim = Connection::Simulate->new(); $sim->print_information(); my $con = Connection->new(); $con->print_information(); print("Done\n"); exit(0); # EOF
Direct Responses: 3735 | Write a response
Posted on Fri Dec 8 19:57:29 2006 by gaetan in response to 3734
Re: OIO and default value
Yeap, it it 6:50pm in France. I will have to leave my office in a few minutes

OK, the previous code is working, but when applying your "recipe" for my program, it does not work

If you add some lines in the code, the program fails

#!/usr/bin/perl use strict; use warnings; package Connection; { use Object::InsideOut; my @bidule :Field :Set(name => 'set_bidule', restricted => 1); my @truc # here the 3 added lines :Field :Arg(name => "truc"); sub init :Init { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 0; } # Store value $self->set(\@bidule, $args->{'bidule'}); } sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]\n"; } } package Connection::Simulate; { use Object::InsideOut('Connection'); sub pre :PreInit { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 1; } } } package main; my $sim = Connection::Simulate->new(); $sim->print_information(); my $con = Connection->new(); $con->print_information(); print("Done\n"); exit(0); # EOF
I obtain the following result:
{go}606: perl ~/tmp/toto.pl Bidule: 0 Bidule: 0 Done

I need help (only for next Monday ;-))

Gaetan
Direct Responses: 3738 | Write a response
Posted on Fri Dec 8 20:57:12 2006 by jdhedden in response to 3735
Re: OIO and default value
The reason that adding @truc changed the results is a bit involved. If a class has no :InitArgs hash and no fields with :Arg, then its :Init subroutine gets all initialization parameters. Otherwise, the init params are filtered. (I'll work on adding some explanation of this to the POD.)

Therefore, by adding @trun which has an :Arg attribute, you need to also add a :InitArgs hash in order to get 'bidule':
#!/usr/bin/perl use strict; use warnings; package Connection; { use Object::InsideOut; my @bidule :Field :Set(name => 'set_bidule', restricted => 1); my @truc :Field :Arg(name => 'truc'); my %init_args :InitArgs = ( 'bidule' => '' ); sub init :Init { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 0; } # Store value $self->set(\@bidule, $args->{'bidule'}); } sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]\n"; } } package Connection::Simulate; { use Object::InsideOut('Connection'); sub pre :PreInit { my ($self, $args) = @_; # Set default, if needed if (! exists($args->{'bidule'})) { $args->{'bidule'} = 1; } } } package main; my $sim = Connection::Simulate->new(); $sim->print_information(); my $con = Connection->new(); $con->print_information(); print("Done\n"); exit(0); # EOF
However, now that I've added the new :Default(...) attribute in v3.04, you could use:
#!/usr/bin/perl use strict; use warnings; package Connection; { use Object::InsideOut; my @bidule :Field :Def(0) :Set(name => 'set_bidule', restricted => 1); my @truc :Field :Arg(name => 'truc'); sub print_information { my $obj = shift; print "Bidule: $bidule[$$obj]\n"; } } package Connection::Simulate; { use Object::InsideOut('Connection'); sub init :Init { my ($self, $args) = @_; $self->set_bidule(1); } } package main; my $sim = Connection::Simulate->new(); $sim->print_information(); my $con = Connection->new(); $con->print_information(); print("Done\n"); exit(0); # EOF
Direct Responses: 3745 | Write a response
Posted on Sat Jan 1 01:19:49 2000 by gaetan in response to 3738
Re: OIO and default value
Hello

It works with the new Default attribute (this interface is cleaner) .... but not in all cases:

The default value I would like to set to my attribute is an object (and not a constant)
my @attr : Field : Default(my::defined::object->new()) ;

Would it be possible to eval the :Default attribute parameter ? If the eval fails, then the attribute is initialized as if the eval was not called.

Gaetan
Direct Responses: 3746 | Write a response
Posted on Tue Dec 12 23:07:18 2006 by jdhedden in response to 3745
Re: OIO and default value
No. You cannot set a default value that needs to be eval'ed at the time of assignment. This is because you could specify a code ref as a default value, and eval'ing it would cause the code ref to be executed which would not give the same result.

See 'Parameter Preprocessing' in the POD which can used for 'Providing a dynamically-determined default value'.
Direct Responses: 3752 | Write a response
Posted on Wed Dec 13 10:16:03 2006 by gaetan in response to 3746
Re: OIO and default value
Jerry, I don't undestand your answer.
If I write
my @attr : Field : Default(sub { print "this is a code ref" }) ;
I would like my attribute to be initialized with the code ref ! Do you have an counterexample ?
Gaetan
Direct Responses: 3754 | 3755 | Write a response
Posted on Wed Dec 13 15:43:47 2006 by jdhedden in response to 3752
Re: OIO and default value
Defaults are specified in 3 places: The :InitArgs hash, the :Args attribute and the :Default attribute. They all need to work consistent with each other. Because :InitArgs are 'compiled', they can not be 'eval'ed at assignment time.
my %init_args :InitArgs = ( 'cref' => { 'field' => \@cref, 'default' => sub { return 'foo' }, }, );
In the above, because the default is a code ref, you cannot do a string eval on it.

Additionally, defaults are currently 'static' data. Implementing some sort of eval mechanism to get dynamic defaults would cause incompatibilities.

Therefore, you'll need to contend with preprocessing to do what you want. That's one of the reasons it was provided.
Write a response
Posted on Wed Dec 13 15:55:59 2006 by jdhedden in response to 3752
Re: OIO and default value
And just to make sure I answered your question, using:
my @attr :Field :Default(sub { print "this is a code ref" });
will assign a code ref as a default. However, your other example:
my @attr :Field :Default(my::defined::object->new());
will result in a single 'my::defined::object' object being generated, and then that same object being assigned every time a default is needed. The same would occur with an :InitArgs hash:
my %init_args :InitArgs = ( 'attr' => { 'field' => \@attr, 'default' => my::defined::object->new(), }, );
Now, if the 'single object reassigned for each default' is what you want, then I apologize for not understanding your question in the first case. However, if you want a different object generated by the code 'my::defined::object->new()' each time a default is needed, then you'll need to do preprocessing for that.

I hope I've answered everything for you. Sorry if I caused any confusion.
Write a response