How to detect an undefined SV* value in XS?

J

J. Romano

Dear Perl community,

I have a problem here and I'm hoping that someone can help.

I am writing some XS code, and I need to detect whether a value
accessed through an SV pointer is defined or undefined. I read
"perldoc perlapi", and the closest I could find was this excerpt:

SvTRUE Returns a boolean indicating whether Perl would
evaluate the SV as true or false, defined or unde-
fined. Does not handle 'get' magic.

bool SvTRUE(SV* sv)

This is great for discovering a variable's boolean value, but I need
some way of knowing if an SV* was undefined when it was passed in. I
suppose I could always do this:

char *valueStr;
STRLEN valueLen;
valueStr = SvPV(*value, valueLen);

and then valueStr would be "" and valueLen would be 0 if *value
contains an undefined value. However, valueStr and valueLen would
both be the same thing if *value pointed to an empty string. The
line:

bool truthValue = SvTRUE(*value);

returns false for both undefined values and empty strings, which means
that it can't differentiate between them, either.

So I can't figure out how to check to see if an SV* value is
defined or not. Any test I perform (with SvPV, SvTRUE, or SvIV) gives
the same results when used with empty strings and undefined values.
This causes a problem if I need to know for sure which was passed in.

For those interested, I tried these commands:
perl -MDevel::peek -e '$a = ""; Dump($a)'
SV = PV(0x80f5a84) at 0x80fc9f8
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x8102a70 ""\0
CUR = 0
LEN = 1
perl -MDevel::peek -e '$a = undef; Dump($a)'
SV = NULL(0x0) at 0x80fc9f8
REFCNT = 1
FLAGS = ()

Unfortunately, I'm not too familiar with Devel::peek to really know
how to use its "Dump" output to my advantage. I see that with an
undefined value, SV equals NULL, but according to the debugger, it's
definitely set to some non-NULL value. Likewise, when I dump an empty
string, the "Dump" output says that LEN equals 1, but according when I
make the following call:

valueStr = SvPV(*value, valueLen);

valueLen gets set to zero (as it does with an undefined value).

Maybe the key to checking an undefined value is to check the FLAGS?
Or is there an easier way to do this that I'm not aware of?

Any help would be appreciated.

Thank you.

-- Jean-Luc
 
S

Sisyphus

J. Romano said:
Dear Perl community,

I have a problem here and I'm hoping that someone can help.

I am writing some XS code, and I need to detect whether a value
accessed through an SV pointer is defined or undefined. I read
"perldoc perlapi", and the closest I could find was this excerpt:

SvTRUE Returns a boolean indicating whether Perl would
evaluate the SV as true or false, defined or unde-
fined. Does not handle 'get' magic.

bool SvTRUE(SV* sv)

This is great for discovering a variable's boolean value, but I need
some way of knowing if an SV* was undefined when it was passed in. I
suppose I could always do this:

char *valueStr;
STRLEN valueLen;
valueStr = SvPV(*value, valueLen);

and then valueStr would be "" and valueLen would be 0 if *value
contains an undefined value. However, valueStr and valueLen would
both be the same thing if *value pointed to an empty string. The
line:

bool truthValue = SvTRUE(*value);

returns false for both undefined values and empty strings, which means
that it can't differentiate between them, either.

So I can't figure out how to check to see if an SV* value is
defined or not. Any test I perform (with SvPV, SvTRUE, or SvIV) gives
the same results when used with empty strings and undefined values.
This causes a problem if I need to know for sure which was passed in.

For those interested, I tried these commands:



SV = PV(0x80f5a84) at 0x80fc9f8
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x8102a70 ""\0
CUR = 0
LEN = 1



SV = NULL(0x0) at 0x80fc9f8
REFCNT = 1
FLAGS = ()

Unfortunately, I'm not too familiar with Devel::peek to really know
how to use its "Dump" output to my advantage. I see that with an
undefined value, SV equals NULL, but according to the debugger, it's
definitely set to some non-NULL value. Likewise, when I dump an empty
string, the "Dump" output says that LEN equals 1, but according when I
make the following call:

valueStr = SvPV(*value, valueLen);

valueLen gets set to zero (as it does with an undefined value).

Yep - the trailing '\0' is being ignored in that function call, but
included in 'LEN'.
Maybe the key to checking an undefined value is to check the FLAGS?
Or is there an easier way to do this that I'm not aware of?

From within the XS code I think you need to examine the 'ANY' field
using the SvANY macro - which you'll find in sv.h, though it's not
documented *anywhere* afaict.

if(SvANY(sv) == NULL) printf("It's undef");
else printf("It aint undef");

Cheers,
Rob
 
B

Ben Morrow

Quoth (e-mail address removed) (J. Romano):
Dear Perl community,

I have a problem here and I'm hoping that someone can help.

I am writing some XS code, and I need to detect whether a value
accessed through an SV pointer is defined or undefined.

I *believe* that all you need to do is

SV *sv;

if (sv != &PL_sv_undef) {
...
}

.. If I am wrong here I would appreciate correction... :)

Ben
 
J

jl_post

Sisyphus said:
From within the XS code I think you need to examine
the 'ANY' field using the SvANY macro - which you'll
find in sv.h, though it's not documented *anywhere*
afaict.

if(SvANY(sv) == NULL) printf("It's undef");
else printf("It aint undef");


Thanks for the response, Rob.

I looked around a little more and I was able to find what I was
looking for. It turns out that the "sv.h" header file defines a value
called SVt_NULL that is returned by the SvTYPE() macro. So I can do
what I want like this:

if (SvTYPE(sv) == SVt_NULL) printf("It's undef");
else printf("It aint undef");

And just like SvANY(), SVt_NULL isn't mentioned anywhere in "perldoc
perlapi". Go figure.

I'll test out your code soon and see if it works.
Thanks again, Rob.

-- Jean-Luc
 
T

Tassilo v. Parseval

Also sprach J. Romano:
Dear Perl community,

I have a problem here and I'm hoping that someone can help.

I am writing some XS code, and I need to detect whether a value
accessed through an SV pointer is defined or undefined. I read
"perldoc perlapi", and the closest I could find was this excerpt:

SvTRUE Returns a boolean indicating whether Perl would
evaluate the SV as true or false, defined or unde-
fined. Does not handle 'get' magic.

bool SvTRUE(SV* sv)

This is great for discovering a variable's boolean value, but I need
some way of knowing if an SV* was undefined when it was passed in. I
suppose I could always do this:

char *valueStr;
STRLEN valueLen;
valueStr = SvPV(*value, valueLen);

and then valueStr would be "" and valueLen would be 0 if *value
contains an undefined value. However, valueStr and valueLen would
both be the same thing if *value pointed to an empty string.

Right. Use SvOK to check whether an SV is defined.
The
line:

bool truthValue = SvTRUE(*value);

returns false for both undefined values and empty strings, which means
that it can't differentiate between them, either.

So I can't figure out how to check to see if an SV* value is
defined or not. Any test I perform (with SvPV, SvTRUE, or SvIV) gives
the same results when used with empty strings and undefined values.
This causes a problem if I need to know for sure which was passed in.

For those interested, I tried these commands:

SV = PV(0x80f5a84) at 0x80fc9f8
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x8102a70 ""\0
CUR = 0
LEN = 1

SV = NULL(0x0) at 0x80fc9f8
REFCNT = 1
FLAGS = ()

Unfortunately, I'm not too familiar with Devel::peek to really know
how to use its "Dump" output to my advantage. I see that with an
undefined value, SV equals NULL, but according to the debugger, it's
definitely set to some non-NULL value. Likewise, when I dump an empty
string, the "Dump" output says that LEN equals 1, but according when I
make the following call:

valueStr = SvPV(*value, valueLen);

valueLen gets set to zero (as it does with an undefined value).

LEN (corresponding to SvLEN) returns the size of the character buffer in
an SV. CUR (SvCUR) on the other hand is the length of the string stored
inside which is usually LEN-1.

The output of Devel::peek::Dump is quite straight-forward really. It
tells you how an SV looks on the inside which can be an invaluable debug
help. In order to understand it, you have to know what the various
flags in sv.h mean because they ultimately determine how perl treats an
SV:

ethan@ethan:~$ perl -MDevel::peek -e '$a = 1; $a = 0.02; $a = "3";
Dump($a)'
SV = PVNV(0x817c970) at 0x8160b30
REFCNT = 1
FLAGS = (POK,pPOK)
IV = 1
NV = 0.02
PV = 0x815d4a0 "3"\0
CUR = 1
LEN = 2

In $a, three different values have been stored (and they are all present
at the same time). The POK tells perl that only the PV value (the
string) should be used. But it doesn't have to be like that:

ethan@ethan:~$ perl -MScalar::Util=dualvar -MDevel::peek -e '$a = 0.02; $a = dualvar(1, "3"); Dump($a)'
SV = PVNV(0x81d9a48) at 0x814cc6c
REFCNT = 1
FLAGS = (IOK,POK,pIOK,pPOK)
IV = 1
NV = 0.02
PV = 0x815d4d0 "3"\0
CUR = 1
LEN = 2

The same three values, only this time IOK and POK are set so perl will
use the IV slot in numeric context and PV otherwise.

So the flags such as SVf_(IOK|NOK|POK|ROK) tell perl what type of value
it should expect to find in the SV. Other flags are related to garbage
collection (e.g. SVf_PADMY which is only set on lexicals and indicates
that the SV is subject to refcounting and garbage-collecting).
Maybe the key to checking an undefined value is to check the FLAGS?
Or is there an easier way to do this that I'm not aware of?

Yes, it's all in the flags. SvOK checks for the pseudo-flags SVf_OK
which is

#define SVf_OK (SVf_IOK|SVf_NOK|SVf_POK|SVf_ROK| \
SVp_IOK|SVp_NOK|SVp_POK)

so from an XS point of view, a value is defined if it is either an
integer, a double, a string or a reference.

Tassilo
 
X

xhoster

Ben Morrow said:
Quoth (e-mail address removed) (J. Romano):

I *believe* that all you need to do is

SV *sv;

if (sv != &PL_sv_undef) {
...
}

. If I am wrong here I would appreciate correction... :)

I think you are wrong, but I don't know how to demonstrate it.
I think that PL_sv_undef is only way (out of many) of being undefined.

Xho
 
T

Tassilo v. Parseval

Also sprach Ben Morrow:
Quoth (e-mail address removed) (J. Romano):

I *believe* that all you need to do is

SV *sv;

if (sv != &PL_sv_undef) {
...
}

. If I am wrong here I would appreciate correction... :)

PL_sv_undef is only a special kind of undef created statically for the
sake of performance and convenience. It has some special properties such
as being read-only and having an artifically high refcount. Devel::peek
will tell you:

ethan@ethan:~$ perl -MDevel::peek
my $a;
Dump(undef);
Dump($a);
SV = NULL(0x0) at 0x814c5f4
REFCNT = 2147483625
FLAGS = (READONLY)
SV = NULL(0x0) at 0x814cc6c
REFCNT = 1
FLAGS = (PADBUSY,PADMY)

Most undefs occuring in Perl code are actually not pointers to
PL_sv_undef but something else. Only undef() and Perl built-ins
documentatedly returning undef will give you this special undefined
value.

Tassilo
 
S

Sisyphus

Ben said:
Quoth (e-mail address removed) (J. Romano):



I *believe* that all you need to do is

SV *sv;

if (sv != &PL_sv_undef) {
...
}

. If I am wrong here I would appreciate correction... :)

Even if sv *is* set to &PL_sv_undef, that seems to be an incorrect way
to test for the condition. Simplest way to check on one's speculations
is just to slap up an Inline C script:

use warnings;
use Inline C => Config =>
BUILD_NOISY => 1; # to make sure we get to
# see compiler warnings

use Inline C => <<'END_OF_C_CODE';

void set_undef(SV * a) {

sv_setsv(a, &PL_sv_undef);

if(a == &PL_sv_undef)
printf("A valid means of testing for &PL_sv_undef\n");

else printf("An INVALID means of testing for &PL_sv_undef\n");

}

END_OF_C_CODE

$x = 23;
set_undef($x);
if($x == undef) {print "OK\n"}

__END__

For me that produces:

An INVALID means of testing for &PL_sv_undef
Use of uninitialized value in numeric eq (==) at try.pl line 23.
Use of uninitialized value in numeric eq (==) at try.pl line 23.
OK

Not sure why the warning appears twice - however it's nice to be able to
trick perl into spouting nonsense :)

Cheers,
Rob
 
S

Sisyphus

I looked around a little more and I was able to find what I was
looking for. It turns out that the "sv.h" header file defines a value
called SVt_NULL that is returned by the SvTYPE() macro. So I can do
what I want like this:

if (SvTYPE(sv) == SVt_NULL) printf("It's undef");
else printf("It aint undef");

And just like SvANY(), SVt_NULL isn't mentioned anywhere in "perldoc
perlapi". Go figure.

At least the SvTYPE macro gets a mention .... but the perlapi
documentation is notorious for being incomplete.

I think either macro will suit your purpose ('SvTYPE' is the more
intuitive choice). As I mentioned in another post in this thread,
simplest way to test things out is with an Inline C script.

Cheers,
Rob
 
J

jl_post

Ben Morrow said:
I *believe* that all you need to do is
if (sv != &PL_sv_undef)

If I am wrong here I would appreciate correction... :)


I don't think that's correct. I might be wrong, but I think that
&PL_sv_undef is a macro that's used when one wants to return an
undefined value, either by pushing it onto the return stack or setting
RETVAL to it, like this:

RETVAL = &PL_sv_undef;

Thanks to the replies of other posters, I now know of three
different ways to check for an undefined value:

if (SvTYPE(sv) == SVt_NULL) printf("Undefined");
# Contributed by Rob (Sisyphus):
if (SvANY(sv) == NULL) printf("Undefined");
# Contributed by Tassilo v. Parseval:
if (SvOK(sv) == false) printf("Undefined");

Of these three methods, only SvOK is documented in "perldoc
perlapi", which is worth considering if portability is a concern. But
I tested all three methods, and they all seem to work.
Thanks for all who replied!

-- Jean-Luc
 
S

Sisyphus

Sisyphus said:
use warnings;
use Inline C => Config =>
BUILD_NOISY => 1; # to make sure we get to
# see compiler warnings

use Inline C => <<'END_OF_C_CODE';

void set_undef(SV * a) {

sv_setsv(a, &PL_sv_undef);

if(a == &PL_sv_undef)
printf("A valid means of testing for &PL_sv_undef\n");

else printf("An INVALID means of testing for &PL_sv_undef\n");

}

END_OF_C_CODE

$x = 23;
set_undef($x);
if($x == undef) {print "OK\n"}

__END__

For me that produces:

An INVALID means of testing for &PL_sv_undef
Use of uninitialized value in numeric eq (==) at try.pl line 23.
Use of uninitialized value in numeric eq (==) at try.pl line 23.
OK

Not sure why the warning appears twice - however it's nice to be able to
trick perl into spouting nonsense :)

Cheers,
Rob

Doh!! The warning appears twice because there are 2 "uninitialized
values" ... and it's not nonsense ... and it would be more sensible to
check that $x is !defined, than to check that $x == undef.

Sorry for that bit of noise. (Hope that's the full extent of it.)

Cheers,
Rob
 
J

jl_post

Ben Morrow said:
I *believe* that all you need to do is
if (sv != &PL_sv_undef)

If I am wrong here I would appreciate correction... :)


I don't think that's correct. I might be wrong, but I think that
&PL_sv_undef is a macro that's used when one wants to return an
undefined value, either by pushing it onto the return stack or setting
RETVAL to it, like this:

RETVAL = &PL_sv_undef;

Thanks to the replies of other posters, I now know of three
different ways to check for an undefined value:

if (SvTYPE(sv) == SVt_NULL) printf("Undefined");
# Contributed by Rob (Sisyphus):
if (SvANY(sv) == NULL) printf("Undefined");
# Contributed by Tassilo v. Parseval:
if (SvOK(sv) == false) printf("Undefined");

Of these three methods, only SvOK is documented in "perldoc
perlapi", which is worth considering if portability is a concern. But
I tested all three methods, and they all seem to work.
Thanks for all who replied!

-- Jean-Luc
 
J

jl_post

Ben Morrow said:
I *believe* that all you need to do is
if (sv != &PL_sv_undef)

If I am wrong here I would appreciate correction... :)


I don't think that's correct. I might be wrong, but I think that
&PL_sv_undef is a macro that's used when one wants to return an
undefined value, either by pushing it onto the return stack or setting
RETVAL to it, like this:

RETVAL = &PL_sv_undef;

Thanks to the replies of other posters, I now know of three
different ways to check for an undefined value:

if (SvTYPE(sv) == SVt_NULL) printf("Undefined");
# Contributed by Rob (Sisyphus):
if (SvANY(sv) == NULL) printf("Undefined");
# Contributed by Tassilo v. Parseval:
if (SvOK(sv) == false) printf("Undefined");

Of these three methods, only SvOK is documented in "perldoc
perlapi", which is worth considering if portability is a concern. But
I tested all three methods, and they all seem to work.
Thanks for all who replied!

-- Jean-Luc
 
X

xhoster

Ben Morrow said:

I don't think that's correct. I might be wrong, but I think that
&PL_sv_undef is a macro that's used when one wants to return an
undefined value, either by pushing it onto the return stack or setting
RETVAL to it, like this:

RETVAL = &PL_sv_undef;

Thanks to the replies of other posters, I now know of three
different ways to check for an undefined value:

if (SvTYPE(sv) == SVt_NULL) printf("Undefined");
# Contributed by Rob (Sisyphus):
if (SvANY(sv) == NULL) printf("Undefined");
# Contributed by Tassilo v. Parseval:
if (SvOK(sv) == false) printf("Undefined");

Of these three methods, only SvOK is documented in "perldoc
perlapi", which is worth considering if portability is a concern. But
I tested all three methods, and they all seem to work.
Thanks for all who replied!

SvANY doesn't seem to work properly for me.

It claims a variable that was once defined but is now undef as if it
were defined. It also treats array slots which are off the end of the
array differently than those "inside" the array (but never assigned to).

Xho


use strict;
use Inline 'C' ;

my @x;
$x[3]=5;
$x[1]=4;
$x[1]=undef;
$x[2]='';

my $h=4;
$h=undef;
print foo($h);

foreach (0..6) {
print foo($x[$_]);
};

__DATA__
__C__

int foo(SV* sv) {
if (SvANY(sv) == NULL ) return 1;
return 0;
};
 
S

Sisyphus

SvANY doesn't seem to work properly for me.

SvTYPE seems to be produces identical results. Seems to me that they're
both functioning correctly - it's just that the fields being examined
are not being set as (you and?) I expect.
It claims a variable that was once defined but is now undef as if it
were defined. It also treats array slots which are off the end of the
array differently than those "inside" the array (but never assigned to).

Devel::peek can be put to good use here.
use strict; use Devel::peek;
use Inline 'C' ;

my @x;
$x[3]=5;
$x[1]=4;
$x[1]=undef;
$x[2]='';

my $h=4;

Dump($h);
print "##############\n";
$h=undef;

Dump($h);
print "##############\n";

# You'll note that the only change is to
# the flags - so, yes, you probably do
# need to check the flags to detect this.
print foo($h);

foreach (0..6) {
print foo($x[$_]);
};

__DATA__
__C__

int foo(SV* sv) {
if (SvANY(sv) == NULL ) return 1;
return 0;
};

In the case of arguments received from "off the end of the array" I
don't know how you should set about detecting them. Perhaps you can work
it out by looking at what gets Dump()ed. It's probably best to make sure
that they can't get passed in to foo(). If you need a definitive answer
to that problem, and you don't get it here, you could try mailing the XS
list at perl-xs at perl.org. (See http://lists.perl.org if you want to
subscribe.)

Cheers,
Rob
 
T

Tassilo v. Parseval

Also sprach (e-mail address removed):
SvANY doesn't seem to work properly for me.

Of course it doesn't. It's just not the proper way to test for
undefinedness.
It claims a variable that was once defined but is now undef as if it
were defined.

A variable once defined and then undefed does keep its previous value,
as can be verified easily:

ethan@ethan:~$ perl -MDevel::peek
$a = 42;
$a = undef;
Dump($a);
^D
SV = IV(0x8162f74) at 0x8160b90
REFCNT = 1
FLAGS = ()
IV = 42

The IV slot is still there which means that SvANY can't be null. From a
technical point of view this is the proper way to do it as it is more
efficient to reset flags than to clean up an SV struct.

I said elsewhere to use SvOK as it will do what you want, namely check
the flags of the SV. I am not entirely sure why you insist on doing it
the hard and wrong way.

Tassilo
 
S

Sisyphus

Tassilo said:
I said elsewhere to use SvOK as it will do what you want

My perlapi docs tell me that SvOK "returns a boolean indicating whether
the value is an SV". I had wondered previously just what that meant ...
now I know.

(Sorry - I missed that piece of advice in your earlier post. I think the
OP might have done the same.)

Cheers,
Rob
 
J

jl_post

(e-mail address removed) replied:
SvANY doesn't seem to work properly for me.

It claims a variable that was once defined but is now undef as if it
were defined. It also treats array slots which are off the end of the
array differently than those "inside" the array (but never assigned
to).


Good find. Because of what you said, I tested the three ways I
listed above, and I found out that SvTYPE suffered from the same
problem. SvOK, however, still worked without any problems.

So that leaves only one way that I know of for checking for an
undefined value, and that's the solution provided by Tassilo v.
Parseval:

if (SvOK(sv) == false) printf("Undefined");

Since this is the only solution that seems to "work correctly" in
all cases, maybe that's the reason that it's the only one mentioned in
"perldoc perlapi".

It would make more sense to me if it were named SvDEFINED instead of
SvOK. Of course, there could very well be a reason it's named that
(that I'm just not aware of).

Regardless, thanks to everyone who has given input. It's very much
appreciated!

-- Jean-Luc
 
X

xhoster

Tassilo v. Parseval said:
Also sprach (e-mail address removed):

Of course it doesn't. It's just not the proper way to test for
undefinedness.

Right, that is what I said.
I said elsewhere to use SvOK as it will do what you want, namely check
the flags of the SV. I am not entirely sure why you insist on doing it
the hard and wrong way.

I don't insist on doing it any way. I was just notifying the person I
replied to that what he assumes works doesn't actually work.

Xho
 
S

Sisyphus

Sisyphus said:
use warnings;
use Inline C => Config =>
BUILD_NOISY => 1; # to make sure we get to
# see compiler warnings

use Inline C => <<'END_OF_C_CODE';

void set_undef(SV * a) {

sv_setsv(a, &PL_sv_undef);

Another thread, and another list - and it has just been drawn to my
attention that's not the right way to assign PL_sv_undef to an SV. The
above line should be replaced by:

a = &PL_sv_undef
if(a == &PL_sv_undef)
printf("A valid means of testing for &PL_sv_undef\n");

else printf("An INVALID means of testing for &PL_sv_undef\n");

}

I haven't tested, but I expect one would then find that the script
reports "A valid means of testing for &PL_sv_undef".

I hope that's correct - if not I'll make another correction tomorrow night.

Egg is good for the facial complexion .... right ???

Cheers,
Rob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top