Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tied HASH as boolean always return false. #6621

Closed
p5pRT opened this issue Jul 14, 2003 · 23 comments
Closed

Tied HASH as boolean always return false. #6621

p5pRT opened this issue Jul 14, 2003 · 23 comments
Labels

Comments

@p5pRT
Copy link

@p5pRT p5pRT commented Jul 14, 2003

Migrated from rt.perl.org#22973 (status was 'resolved')

Searchable as RT22973$

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 14, 2003

From gm@virtuasites.com.br

A tied HASH when used as a boolean always return it as false!

Example​:

  my %hash ;

  tie(%hash , 'TiedHash') ;

  print "key​: $hash{key}\n" ;

  if ( %hash ) { print "BOOL​: 1\n" ;}
  else { print "BOOL​: 0\n" ;}

  package TiedHash ;

  sub TIEHASH { bless({}, __PACKAGE__ ) ;}

  sub FETCH { return( 'fetch_val' ) ;}

But if you set some data before set it as HASH not​:

  my %hash = (1) ;

  tie(%hash , 'TiedHash') ;

  if ( %hash ) { print "BOOL​: 1\n" ;}
  else { print "BOOL​: 0\n" ;}

  package TiedHash ;

  sub TIEHASH { bless({}, __PACKAGE__ ) ;}

I know that some Perl developers already know that. But this isn't
documented and
we can see that doesn't work in a beatiful way, or like it should be.

What I think that can be done is to add a new TIE function BOOLEAN to catch
the
query of the boolean value, and if the functions doesn't exists return false
to mantain the compatibility.

I will look at Perl source to send some path for the new function. But if
the change for the new function is not interesting for you, at least change
the POD and add some commet about that.

Regards,
Graciliano M. P.

PS​: perlbug script never works for Win32!


Summary of my perl5 (revision 5 version 8 subversion 0) configuration​:
  Platform​:
  osname=MSWin32, osvers=4.0, archname=MSWin32-x86-multi-thread
  uname=''
  config_args='undef'
  hint=recommended, useposix=true, d_sigaction=undef
  usethreads=undef use5005threads=undef useithreads=define
usemultiplicity=define
  useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
  use64bitint=undef use64bitall=undef uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cl', ccflags
='-nologo -Gf -W3 -MD -Zi -DNDEBUG -O1 -DWIN32 -D_CONSOLE -DNO_STRICT -DHAVE
_DES_FCRYPT -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -DUSE_PERLIO -DPERL
_MSVCRT_READFIX',
  optimize='-MD -Zi -DNDEBUG -O1',
  cppflags='-DWIN32'
  ccversion='', gccversion='', gccosandvers=''
  intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
  d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=10
  ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='__int64',
lseeksize=8
  alignbytes=8, prototype=define
  Linker and Libraries​:
  ld='link', ldflags
'-nologo -nodefaultlib -debug -opt​:ref,icf -libpath​:"c​:\perl\lib\CORE" -ma
chine​:x86'
  libpth="C​:\perl\lib\CORE" "C​:\Arquivos de programas\Microsoft Visual
Studio\VC98\Lib"
  libs= oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib
uuid.lib wsock32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib
msvcrt.lib
  perllibs= oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib
uuid.lib wsock32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib
msvcrt.lib
  libc=msvcrt.lib, so=dll, useshrplib=yes, libperl=perl58.lib
  gnulibc_version='undef'
  Dynamic Linking​:
  dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
  cccdlflags=' ',
lddlflags='-dll -nologo -nodefaultlib -debug -opt​:ref,icf -libpath​:"c​:\perl
\lib\CORE" -machine​:x86'

Characteristics of this binary (from libperl)​:
  Compile-time options​: MULTIPLICITY USE_ITHREADS USE_LARGE_FILES
PERL_IMPLICIT_CONTEXT PERL_IMPLICIT_SYS
  Locally applied patches​:
  Perl Build 805
  Built under MSWin32
  Compiled at Mar 15 2003 19​:25​:16
  @​INC​:
  C​:/perl/lib
  C​:/perl/site/lib
  .

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @schwern

FWIW, 5.8.1 RC2 hash changed the behavior of testing the boolean condition of a tied
hash to an error​:

  Can't provide tied hash usage; use keys(%hash) to test if empty at - line 7.

until there is some way of returning bucket information from a tied hash (ie. what a
hash returns in scalar context).

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

@schwern - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From gm@virtuasites.com.br

Subject​: [perl #22973] Tied HASH as boolean always return false.

FWIW, 5.8.1 RC2 hash changed the behavior of testing the boolean condition
of a tied
hash to an error​:

Noooo!!! Now all my scripts that uses tied HASH will die!!!

For example, in mod_perl, I use a module that handles the incoming formulary
data, separating them in 2 hashes %POST and %GET, and an extra hash %FORM,
where %FORM can access all the keys of %POST and %GET at the same time. But
%FORM is only tied when I receive the 2 types of form, and when I have only
1 type it's just a link in the table to %POST or %GET.

The best thing of %FORM is to can check easy if I have formulary data using​:

  if ( %FORM ) { ... }

... and now this isn't possible, and every thing will die!

Note that to enable the boolean check to %FORM, the HASH that is tied has a
key seted before tie it​:

%hash = (1) ;

tie( \%hash , 'tiehandle' ) ;

Can't provide tied hash usage; use keys(%hash) to test if empty at -
line 7.

OK, but I think that kind of change is not good for a fix release, since
5.8.1 is about that.

And create an erro?! At least keep the compatibility with what we have
before, or make the tie boolean return true always, since if you tie
something you have it!

Please, please, remove the error, and keep what exists before! And when we a
have path for that we change the behavior, insted to say to the user that
what it have is wrong! And what we have isn't wrong, it's just not
implemented in the right way yet.

Other thing. The best thing that should be made without implement the
boolean handler for tied hashes is to add to the POD that to make a tied
HASH return true, you need to have keys in the hash reference that isn't
tied.

until there is some way of returning bucket information from a tied hash
(ie. what a
hash returns in scalar context).

I will look for a path, maybe before release 5.8.1!

Regards,
Graciliano M. P.

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @schwern

On Wed, Jul 16, 2003 at 10​:51​:21AM -0300, Graciliano M. P. (Virtua Sites) wrote​:

Can't provide tied hash usage; use keys(%hash) to test if empty at -
line 7.

OK, but I think that kind of change is not good for a fix release, since
5.8.1 is about that.

I'd tend to agree.

And create an erro?! At least keep the compatibility with what we have
before, or make the tie boolean return true always, since if you tie
something you have it!

That just changes the behavior from wrong to a different kind of wrong.
An *empty* hash will return false in scalar context. So should an empty
tied hash.

What could be done is​:

A) downgrade the error to a warning
B) remove the message a together since its kind of lame as we currently
  have no good fix and people have survived for years with the current
  behavior
C) do what the warning suggests behind the scenes. In boolean context,
  just fallback to using keys(%hash).

I prefer B or C. C remains the Right Thing even if we introduce a true
BUCKET method for tied hashes later on.

--
I know you get this a lot, but what's an unholy fairy like you doing in a
mosque like this?

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @mjdominus

What I think that can be done is to add a new TIE function BOOLEAN
to catch the query of the boolean value, and if the functions
doesn't exists return false to mantain the compatibility.

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

It might also be a KEYS method which can be called in list context or
scalar context when the result of 'keys' or 'values' was needed; if
KEYS was undefined Perl could fall back on using FIRSTKEY and NEXTKEY
as it does now. This would be more work and more code, but also more
useful.

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @schwern

On Wed, Jul 16, 2003 at 05​:35​:45PM -0400, Mark Jason Dominus wrote​:

What I think that can be done is to add a new TIE function BOOLEAN
to catch the query of the boolean value, and if the functions
doesn't exists return false to mantain the compatibility.

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

It might also be a KEYS method which can be called in list context or
scalar context when the result of 'keys' or 'values' was needed; if
KEYS was undefined Perl could fall back on using FIRSTKEY and NEXTKEY
as it does now. This would be more work and more code, but also more
useful.

Trouble is, you need more than just the number of keys to fully emulate the
current behavior of a hash in scalar context.

$ perl -wle '%h = (foo => 42); print scalar %h'
1/8

--
IGNITE THE FIRE MOAT!

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @timj

On Wed, 16 Jul 2003, Michael G Schwern wrote​:

On Wed, Jul 16, 2003 at 05​:35​:45PM -0400, Mark Jason Dominus wrote​:

What I think that can be done is to add a new TIE function BOOLEAN
to catch the query of the boolean value, and if the functions
doesn't exists return false to mantain the compatibility.

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

It might also be a KEYS method which can be called in list context or
scalar context when the result of 'keys' or 'values' was needed; if
KEYS was undefined Perl could fall back on using FIRSTKEY and NEXTKEY
as it does now. This would be more work and more code, but also more
useful.

Trouble is, you need more than just the number of keys to fully emulate the
current behavior of a hash in scalar context.

$ perl -wle '%h = (foo => 42); print scalar %h'
1/8

Since a tied hash does not have to refer to a real hash couldn't
this be emulated simply by returning the result as if the tied
hash was a perfect hash?

--
Tim Jenness
JAC software
http​://www.jach.hawaii.edu/~timj

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @tamias

On Wed, Jul 16, 2003 at 02​:52​:06PM -0700, Michael G Schwern wrote​:

On Wed, Jul 16, 2003 at 05​:35​:45PM -0400, Mark Jason Dominus wrote​:

What I think that can be done is to add a new TIE function BOOLEAN
to catch the query of the boolean value, and if the functions
doesn't exists return false to mantain the compatibility.

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

It might also be a KEYS method which can be called in list context or
scalar context when the result of 'keys' or 'values' was needed; if
KEYS was undefined Perl could fall back on using FIRSTKEY and NEXTKEY
as it does now. This would be more work and more code, but also more
useful.

Trouble is, you need more than just the number of keys to fully emulate the
current behavior of a hash in scalar context.

$ perl -wle '%h = (foo => 42); print scalar %h'
1/8

In fact, for that you don't really need the number of keys at all. :)
It's occupied buckets/allocated buckets.

But precisely emulating the behavior of a hash in scalar context seems a
bit silly, since the behavior is based on the internal implementation of
the hash, and the internals of a tied hash often aren't like a regular
hash.

Ronald

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From @schwern

On Wed, Jul 16, 2003 at 12​:04​:07PM -1000, Tim Jenness wrote​:

Since a tied hash does not have to refer to a real hash couldn't
this be emulated simply by returning the result as if the tied
hash was a perfect hash?

Given that the scalar value of a hash is rather silly, and having a method
to perfectly emulate it would be even sillier, I don't see why not.

--
Cuius rei demonstrationem mirabilem sane detexi hanc subscriptis
exiguitas non caperet.

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From enache@rdslink.ro

On Wed, Jul 16, 2003 at 02​:20​:03PM -0700, Michael G Schwern wrote​:

On Wed, Jul 16, 2003 at 10​:51​:21AM -0300, Graciliano M. P. (Virtua Sites) wrote​:

Can't provide tied hash usage; use keys(%hash) to test if empty at -
line 7.

$ perl -le '{package P; sub TIEHASH {bless{}}} tie my %h, P; print "yeah\n" if scalar %h'
Can't provide tied hash usage; use keys(%hash) to test if empty at -e line 1.
$ perl -le '{package P; sub TIEHASH {bless{}}} tie %h, P; print "yeah\n" if scalar %h'
$

why does Perl issue the error only when %h is in the pad ?
That should be fixed somehow.

What could be done is​:

A) downgrade the error to a warning
B) remove the message a together since its kind of lame as we currently
have no good fix and people have survived for years with the current
behavior
C) do what the warning suggests behind the scenes. In boolean context,
just fallback to using keys(%hash).

I prefer B or C. C remains the Right Thing even if we introduce a true
BUCKET method for tied hashes later on.

Why should perl start doing a lot of crap behind your back (iterate through
an entire tied hash) when you only wanted if know if the hash contained
at least _one_ key ? :-)

Regards,
Adi

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 16, 2003

From enache@rdslink.ro

On Wed, Jul 16, 2003 at 05​:35​:45PM -0400, Mark Jason Dominus wrote​:

What I think that can be done is to add a new TIE function BOOLEAN
to catch the query of the boolean value, and if the functions
doesn't exists return false to mantain the compatibility.

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

Does testing if FIRSTKEY returns something define'd sound reasonable
enough as the boolean test for a tied hash ?

Regards,
Adi

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From enache@rdslink.ro

On Thu, Jul 17, 2003 at 12​:51​:32AM +0100, Dave Mitchell wrote​:

On Thu, Jul 17, 2003 at 02​:21​:39AM +0300, Enache Adrian wrote​:

Why should perl start doing a lot of crap behind your back (iterate through
an entire tied hash) when you only wanted if know if the hash contained
at least _one_ key ? :-)

Why can't a tied hash in a scalar context call FIRSTKEY, and if
that method returns undef, return '0', else return '1/8'. This would at
least solve the 'is the hash empty problem'

Agreed.

Regards,
Adi

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From @schwern

On Thu, Jul 17, 2003 at 02​:46​:10AM +0300, Enache Adrian wrote​:

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

Does testing if FIRSTKEY returns something define'd sound reasonable
enough as the boolean test for a tied hash ?

Yes, that'll do for boolean context. Not sure about non-boolean scalar
contexts.

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From @tamias

On Thu, Jul 17, 2003 at 02​:46​:10AM +0300, Enache Adrian wrote​:

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

Does testing if FIRSTKEY returns something define'd sound reasonable
enough as the boolean test for a tied hash ?

What effect will that have on the hash iterator?

Ronald

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From gm@virtuasites.com.br

The use of HASH as boolean was fixed and the compatibility with the
previous versions of Perl was keeped too.

Changes​: ( File​: pp.c at pp_padhv() )
New handler method FILL added to for a tied HASH.

Behavior​:
First look for the method in the objet, if the method FILL exists, call
it, and the returned value (G_SCALAR) is paste as the scalar/boolean
value of the HASH.

If the method FILL doesn't exists, get the buckets size as the
normal/previous way, to keep the compatibility.

Test Script of Changes​:

  ## To test the previous behavior (when method FILL doesn't exists)​:
  #my %hash = (1) ;
  my %hash ;

  my $tieobj = tie(%hash , 'TiedHash') ;
  if ( %hash ) { print "BOOL​: 1\n" ;}
  else { print "BOOL​: 0\n" ;}
 
  my $buckets = %hash ;
  print "BUCKETS​: $buckets\n" ;

  package TiedHash ;
  sub TIEHASH { bless({}, __PACKAGE__ ) ;}
  ## Try to change the return to a false value​:
  sub FILL { print "FILL>> $_[0]\n" ; return "1/8" ;}

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From gm@virtuasites.com.br

pp.c.diff
109,114c109,130
< 	SV* sv = sv_newmortal();
<         if (SvRMAGICAL(TARG) && mg_find(TARG, PERL_MAGIC_tied))
< 	     Perl_croak(aTHX_ "Can't provide tied hash usage; "
< 			"use keys(%%hash) to test if empty");
< 	if (HvFILL((HV*)TARG))
< 	    Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
---
> 	bool ret_buckets = TRUE ;
> 	MAGIC *mg;
> 	if ( SvRMAGICAL(TARG) && (mg = mg_find(TARG, PERL_MAGIC_tied)) ) {
>       	SV *obj = SvRV(mg->mg_obj);
>       	GV *gv;
>       	CV *cv = NULL;
> 	    if ( (gv = gv_fetchmethod_autoload(SvSTASH(obj), "FILL", FALSE)) && isGV(gv) && (cv = GvCV(gv))) {
> 		   ret_buckets = FALSE ;
> 		   PUSHMARK(SP);
> 		   XPUSHs(SvTIED_obj((SV*)gv, mg));
> 		   XPUSHs(sv_2mortal(newSViv(SvREFCNT(obj)-1)));
> 		   PUTBACK;
> 		   ENTER;
> 		   call_sv((SV *)cv, G_SCALAR);
> 		   LEAVE;
> 		   SPAGAIN;
>         }
> 	}
> 	if (ret_buckets) {
> 		SV* sv = sv_newmortal();
> 		if (HvFILL((HV*)TARG))
> 		   Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
116,118c132,135
< 	else
< 	    sv_setiv(sv, 0);
< 	SETs(sv);
---
> 		else
> 		   sv_setiv(sv, 0);
> 		SETs(sv);
> 		}

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

gm@virtuasites.com.br - Status changed from 'open' to 'resolved'

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From @iabyn

On Thu, Jul 17, 2003 at 02​:21​:39AM +0300, Enache Adrian wrote​:

Why should perl start doing a lot of crap behind your back (iterate through
an entire tied hash) when you only wanted if know if the hash contained
at least _one_ key ? :-)

Why can't a tied hash in a scalar context call FIRSTKEY, and if
that method returns undef, return '0', else return '1/8'. This would at
least solve the 'is the hash empty problem'

--
O Unicef Clearasil!
Gibberish and Drivel!
  - "Bored of the Rings"

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From gm@virtuasites.com.br

Does testing if FIRSTKEY returns something define'd sound reasonable
enough as the boolean test for a tied hash ?

This is a good point, since it's faster to process, instead fo​: (keys
(%hash))

But I still think that the best way is to introduce a new function​: BOOLEAN.
And the user make what it want, just returning a true value (in the format
that it want) or not. And if the BOOLEAN function doesn't exists, make the
behavior works like in 5.8.0, just return FALSE or TRUE depending the
content of the real HASH reference (the previous when it's tied).

And this is not to about to fully emulate the current behavior of a hash!
This is just to can now if a HASH is null or not! No one want to make this​:

  my $hash_buckets_use = %hash ;

We just want to make​:

  if ( %HASH ) { ... }

...and the problem is in the behavior of the HASH as boolean, not to get the
buckets use of a tied HASH, that make no sense!

But looking in the implementation of this in Perl, at pp.c, I saw that is
not good to call a method called BOOLEAN when trying to implement the
behavior. Since the implementation, when checking the boolean value, look
for the buckets size​:

  if (HvFILL((HV*)TARG))
  Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
  (long)HvFILL((HV*)TARG), (long)HvMAX((HV*)TARG) + 1); // return
the buckets use (true)
  else
  sv_setiv(sv, 0); // return buckets as 0 (false)

... The best way is to implement a method that works in the FILL area (if it
uses buckets or not), not in the buckets result/value.

*** I'm working in a path for this, I just need to know what name to use!
So, please, send your best ideas, and note, this is for a function that just
tell if the HASH is null or not, if it FILLs something or not, to follow the
actuall internal structure of the implementation.

Regards,
Graciliano M. P.

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From rick.delaney@rogers.com

On Thu, Jul 17, 2003 at 02​:46​:10AM +0300, Enache Adrian wrote​:

On Wed, Jul 16, 2003 at 05​:35​:45PM -0400, Mark Jason Dominus wrote​:

What I think that can be done is to add a new TIE function BOOLEAN
to catch the query of the boolean value, and if the functions
doesn't exists return false to mantain the compatibility.

It seems to me that this is the wrong approach. I'm not sure what the
right approach is, but I think it would be something more like a
FETCHSIZE method which would return the total number of keys.

Does testing if FIRSTKEY returns something define'd sound reasonable
enough as the boolean test for a tied hash ?

That might screw up someone's iterator pointer. Using the example
FIRSTKEY and NEXTKEY from perltie, this would loop forever​:

  while (($k, $v) = each %h) {
  print "This is for illustration only\n" if %h;
  }

--
Rick Delaney
rick.delaney@​rogers.com

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 17, 2003

From gm@virtuasites.com.br

I have sent the PATH for that (for Perl-5.8.1-RC2)! But this is the 1st time
that I send a PATH to Perl, please, check if everything goes fine, since
this need to be applied before release Perl-5.8.1

Regards,
Graciliano M. P.

@p5pRT p5pRT closed this as completed Nov 10, 2005
@p5pRT
Copy link
Author

@p5pRT p5pRT commented Nov 10, 2005

@rgs - Status changed from 'open' to 'resolved'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant