How to test for valid variable reference ?

J

joenuts

Is it possible for a function to test one of it's passed in variables
(reference to object) for validity?
I would like the displayString( string &obString) function to verify
that obString 1) exists 2) is a valid string object. calling any
methods of said object cause a seg fault if the object doesnt exist.
(code to follow)

#include <string>
#include <iostream>
#include <vector>
using namespace std;

string displayString( string &obString) {
cout << "The display string is " << obString << endl;
return( obString);
}

int main() {

vector<string> obStringList;
string obString;

obString = "test string";
obStringList.push_back( obString);

displayString( obStringList[0]);

displayString( obStringList[1]);

return( 0);
}

localhost cpptest # g++ -Wall main.cpp
localhost cpptest # ./a.out
The display string is test string
Segmentation fault
localhost cpptest #
 
P

Paul Bilnoski

joenuts said:
Is it possible for a function to test one of it's passed in variables
(reference to object) for validity?
I would like the displayString( string &obString) function to verify
that obString 1) exists 2) is a valid string object. calling any
methods of said object cause a seg fault if the object doesnt exist.
(code to follow)

#include <string>
#include <iostream>
#include <vector>
using namespace std;

string displayString( string &obString) {
cout << "The display string is " << obString << endl;
return( obString);
}

int main() {

vector<string> obStringList;
string obString;

obString = "test string";
obStringList.push_back( obString);

displayString( obStringList[0]);

displayString( obStringList[1]);

return( 0);
}

localhost cpptest # g++ -Wall main.cpp
localhost cpptest # ./a.out
The display string is test string
Segmentation fault
localhost cpptest #

The problem is in the call, not in the function. Inside the function is
where the program realizes the variable is invalid. Since you're passing
it by reference, it is the responsibility of the caller to ensure that
the arguments are proper, and since std::vector by default does not
throw errors on out of range access, you should check before calling if
there is a question.

Your displayString should always safely assume that a reference
parameter is valid. Use 'bool std::string::empty()' to see if it has
contents if necessary.

--Paul
 
J

John Harrison

joenuts said:
Is it possible for a function to test one of it's passed in variables
(reference to object) for validity?
I would like the displayString( string &obString) function to verify
that obString 1) exists 2) is a valid string object. calling any
methods of said object cause a seg fault if the object doesnt exist.
(code to follow)

No, sorry.

john
 
K

Kyle

joenuts said:
Is it possible for a function to test one of it's passed in variables
(reference to object) for validity?
I would like the displayString( string &obString) function to verify
that obString 1) exists 2) is a valid string object. calling any
methods of said object cause a seg fault if the object doesnt exist.
(code to follow)

#include <string>
#include <iostream>
#include <vector>
using namespace std;

string displayString( string &obString) {
cout << "The display string is " << obString << endl;
return( obString);
}

int main() {

vector<string> obStringList;
string obString;

obString = "test string";
obStringList.push_back( obString);

displayString( obStringList[0]);

displayString( obStringList[1]);

use obStringList.at( 1 ) instead, as it does bounds checking. It throws
out_of_range
 
B

Bob Hairgrove

Is it possible for a function to test one of it's passed in variables
(reference to object) for validity?
I would like the displayString( string &obString) function to verify
that obString 1) exists 2) is a valid string object. calling any
methods of said object cause a seg fault if the object doesnt exist.
(code to follow)

References automatically guarantee that both (1) and (2) are true
INSIDE the function body, unless you have done something to make the
original object referenced become invalid. Your segfault comes from
trying to access the second element of your vector which has only one
element. The program never makes it to the function the second time
around.

If you use vector::at() instead of vector::eek:perator[], you will get a
C++ exception (std::eek:ut_of_range). Indexing further into a vector than
there are elements with operator[] causes undefined behavior (i.e.
crashes).
#include <string>
#include <iostream>
#include <vector>
using namespace std;

string displayString( string &obString) {
cout << "The display string is " << obString << endl;
return( obString);
}

int main() {

vector<string> obStringList;
string obString;

obString = "test string";
obStringList.push_back( obString);

displayString( obStringList[0]);

displayString( obStringList[1]);

return( 0);
}

localhost cpptest # g++ -Wall main.cpp
localhost cpptest # ./a.out
The display string is test string
Segmentation fault
localhost cpptest #
 
A

Artie Gold

joenuts said:
Is it possible for a function to test one of it's passed in variables its
(reference to object) for validity?
I would like the displayString( string &obString) function to verify
that obString 1) exists 2) is a valid string object. calling any
methods of said object cause a seg fault if the object doesnt exist.
(code to follow)

The problem here is not the string variable, it is the the fact that
you're referencing a vector element that doesn't exist (in this case
`obStringList[1]').

If you use the `at' member of the vector template, it will throw an
`out_of_range' exception, which you can catch.

Remember, references are *always* valid (as distinguished from pointers,
which may be NULL).

[see notes below]
#include <string>
#include <iostream>
#include <vector>
using namespace std;

string displayString( string &obString) {
cout << "The display string is " << obString << endl;
return( obString);
}

int main() {

vector<string> obStringList;
string obString;

obString = "test string";
obStringList.push_back( obString);

displayString( obStringList[0]);

displayString( obStringList[1]);

Whoops. There *is* no obStringList[1], yielding undefined behavior (in
this case a segmenetation fault). Your code never gets as far as the
second call to displayString().

If you instead used obStringList.at(1), you could wrap it in a try catch
block and recover from the exception. [Of course, you could first test
the index against the size of the vector.]
return( 0);
}

localhost cpptest # g++ -Wall main.cpp
localhost cpptest # ./a.out
The display string is test string
Segmentation fault
localhost cpptest #
HTH,
--ag
 
J

joenuts

Thanks all for the quick responses. I understand that it was the out of
bounds call that causes the segfault (essentially). For those who say
that the second function call never happens due to the out of bounds
call in the parameter list, consider the following code (attached ..
slightly modified from original code). *note* from what I can tell, the
function call DOES happen, and any line in the function that doesnt
contain the reference variable (and precedes it) runs correctly. It is
only when trying to execute the code that contains the variable
reference that the program crashes. (food for thought)
Anyways, thanks for the tips. I'm trying to make my functions (methods)
as bulletproof as possible. It seems like the general consensus is that
the CALLER needs to do the validation (when passing references). Any
more tips are appreciated.
Thanks again!
-Joe
#include <string>
#include <iostream>
#include <vector>
using namespace std;

string displayString( string &obString) {
string newString;
cout << "made it here / possible variable check here?" << endl;
// cout << "The display string is " << obString << endl;
return( newString);
}

int main() {

vector<string> obStringList;
string obString;

obString = "test string";
obStringList.push_back( obString);

displayString( obStringList[0]);

displayString( obStringList[1]);

cout << "about to finish the program. Happy Ending!" << endl;

return( 0);
}
localhost cpptest # g++ -Wall main.cpp
localhost cpptest # ./a.out
made it here / possible variable check here?
made it here / possible variable check here?
about to finish the program. Happy Ending!
localhost cpptest #
 
B

Bob Hairgrove

Thanks all for the quick responses. I understand that it was the out of
bounds call that causes the segfault (essentially). For those who say
that the second function call never happens due to the out of bounds
call in the parameter list, consider the following code (attached ..
slightly modified from original code). *note* from what I can tell, the
function call DOES happen, and any line in the function that doesnt
contain the reference variable (and precedes it) runs correctly. It is
only when trying to execute the code that contains the variable
reference that the program crashes. (food for thought)

It's really irrelevant, because the minute you access the non-existant
vector element, you have "undefined behavior". That can mean anything,
from formatting your hard disks to executing the code in
displayString()seemingly correctly. You cannot prohibit ANYTHING once
there is undefined behavior, so you need to take care of it where it
occurs.

[rest snipped]
 
D

Dervish

string displayString( string &obString) {Actually there is some chance to catch rough errors of calling code.
E.g. you can check that:
1) (&obString != 0) - case when somebody called displayString with
reference to string at address 0.
2) If you use debug build than on some platforms there can be some
"magic" pointer values which show you that pointer is invalid -
something like (&obString != 0xCDCDCD). However this is platform
specific - and can be not available.
3) Moreover you can look through std::string implementation and try to
check its internals - very nasty and not portable.

If you really want to do such a check you should develop some registry
of all strings in the program. You can try smart pointer with available
IsValid() method or something like this:
int displayString( int &obString) {
if (MyRegistry::StringIsValid(obString))
cout << "The display string is " << MyRegistry::GetString(obString) <<
endl;
return( obString);
}

However, as discussed, at this point nothing can help your program,
even this "smart pointer"/registry trick.
displayString( obStringList[1]);
 
B

Bob Hairgrove

Actually there is some chance to catch rough errors of calling code.
E.g. you can check that:
1) (&obString != 0) - case when somebody called displayString with
reference to string at address 0.

This is silly. The language prohibits that from happening. If the
compiler *does* let this through, then it's broken.
2) If you use debug build than on some platforms there can be some
"magic" pointer values which show you that pointer is invalid -
something like (&obString != 0xCDCDCD). However this is platform
specific - and can be not available.

But we aren't talking about pointers. We are talking about references.
Pointers are not references, and references are not pointers.
3) Moreover you can look through std::string implementation and try to
check its internals - very nasty and not portable.

Total waste of time. Besides, it has nothing to do with the question
which concerned "test for valid reference". A reference need not be a
reference to std::string, it could be a reference to anything ...
including a type defined in a closed-source third-party library to
which only the interface is available.
If you really want to do such a check you should develop some registry
of all strings in the program. You can try smart pointer with available
IsValid() method or something like this:
int displayString( int &obString) {
if (MyRegistry::StringIsValid(obString))
cout << "The display string is " << MyRegistry::GetString(obString) <<
endl;
return( obString);
}

Wouldn't work if strings can be generated from user input, would it?
And this is *really* unnecessary, and much more error-prone than just
using the simple reference.
However, as discussed, at this point nothing can help your program,
even this "smart pointer"/registry trick.
displayString( obStringList[1]);

Exactly. All the other suggestions are just smoke and mirrors to cover
up the real problem, which is undefined behavior caused by accessing
an element in the vector which doesn't exist.
 
B

Bob Hairgrove

This is silly. The language prohibits that from happening. If the
compiler *does* let this through, then it's broken.

More precisely, you are probably worried about something like the
following (i.e. the dangling reference):

string * pStr = new string("Hello");
string& rStr = *pStr;
delete pStr;
pStr = 0;
// evil code:
displayString(rStr);

By the time you call "delete pStr;", rStr is an invalid reference.
*Any* use of rStr afterwards creates undefined behavior! And once you
have undefined behavior, the best checks in the world won't help you
revert to well-defined behavior inside of displayString(). It is
(remotely) possible that &rStr could be zero, but only because after
there is undefined behavior, anything is possible! It's a big trap to
think that you can write code to get out of undefined behavior once
you are in it.

But, since most people who like to write code comparing the address of
a reference to 0 also seem to believe that "references are just fancy
pointers" (they are not!), you will probably expect the following
internal implementation of the above as a pointer:

string * pStr = new string("Hello");
// implement rStr as a pointer:
string * rStr = pStr;
delete pStr;
pStr = 0; // What does rStr now point to??
// evil code:
// assuming that displayString() now expects a pointer:
displayString(rStr);

Obviously, rStr as a pointer will not compare equal to 0, either. You
are stuck with the same problem given the above situation. However,
with the pointer version, you MUST check the argument ANYWAY because
the language allows passing a null pointer to the function. That is
what the reference buys you.
 
D

Dervish

Pointers are not references, and references are not pointers.
But it possible to convert one to another - and due to this (if design
of interfaces is poor) - bad thing can happen. Look:

#include <iostream>
using namespace std;

string displayString( string &obString) {
if ( &obString==0)
{
cout << "Bad thing happened" << endl;
return "Blablabla";
}
else
{
cout << "The display string is " << obString << endl;
}
return( obString);
}


int main(int, char**)
{
std::string hello("Hello, world!");
srand(time(0));
int canHappen = rand()%2;
std::string *ptr = canHappen?0:&hello;
displayString(*ptr);
return 0;
}
So check of reference can help - but usually it is a sign, that pointer
should be used instead of reference.
Wouldn't work if strings can be generated from user input, would it?
It would work. One can do something like this:
int obString = MyRegistry::ReadStringFromCin();
 
D

deane_gavin

Dervish said:
But it possible to convert one to another - and due to this (if design
of interfaces is poor) - bad thing can happen. Look:

#include <iostream>
using namespace std;

string displayString( string &obString) {
if ( &obString==0)

It is impossible for this condition to be true unless you have already
invoked undefined behaviour, in which case anything you try and do from
that point on is irrelevant.
{
cout << "Bad thing happened" << endl;
return "Blablabla";
}
else
{
cout << "The display string is " << obString << endl;
}
return( obString);
}


int main(int, char**)
{
std::string hello("Hello, world!");
srand(time(0));
int canHappen = rand()%2;
std::string *ptr = canHappen?0:&hello;
displayString(*ptr);

Here you derefernce ptr. If canHappen is zero then ptr is null and
dereferencing it is undefined behaviour. What the displayString
function does or does not try to do about that is irrelevant.
return 0;
}
So check of reference can help - but usually it is a sign, that pointer
should be used instead of reference.

No it can't help, but yes if you want to be able to pass null then a
pointer should be used because there is no way to pass a null
reference.

Gavin Deane
 
A

Andrew Koenig

This is silly. The language prohibits that from happening. If the
compiler *does* let this through, then it's broken.

Really? Why do you think so?

Suppose I write the following:

#include <iostream>

void f(int& foo)
{
if (&foo == 0)
std::cout << "&foo is zero\n";
}

int main()
{
int* p = 0;

f(*p);
return 0;
}

I think you are saying that if I compile and execute this program and it
prints "&foo is zero", that means that my compiler is broken. If so, I
think you are mistaken: I think that if you compile and execute this
program, you cannot draw any inference at all about whether the compiler is
working correctly, regardless of what the program does when executed.

My reasoning is simple: p is a zero pointer, so the effect of evaluating *p
is undefined. So the moment the program has evaluated *p, the
implementation is free to do absoluately anything it wishes, and you cannot
claim it is broken regardless of what it does.

So if you are saying that only a broken compiler would print "&foo is zero"
when given the program above, I think you're mistaken. If you mean
something else, would you mind clarifying what it is?
 
B

Bob Hairgrove

Really? Why do you think so?

I posted an additional message trying to be more specific, and I
believe we are in agreement, but I implied that the compiler might
somehow be an accomplice to undefined behavior, and that was wrong. By
"let that through", I meant that the language guarantees that the
address of objString can never be zero with well-defined behavior, so
checking it is useless.

Here's an instant replay, in case you hadn't seen it before your reply
came through: The C++ standard says that a reference must be
initialized with a valid object. OK, so as long as the lifetime of
that object, we have well-defined behavior WRT the reference (i.e
assuming nothing else causes UDB). When (and if) the code gets to the
body of displayString(), there are only two possibilities: (a)
behavior is still well-defined, and therefore obString must be a valid
reference; (b) something has happened to the referenced object
*before* the code gets to the body of displayString(), and the
reference isn't valid anymore. The instant we use that reference in
any way, we have undefined behavior (dangling reference).

My mistake was in choosing the wording "let that through". With UDB,
it is also possible, of course, that the compiler "lets that through".
As you say, that doesn't mean that the compiler is broken. My point
was that it is useless to try to prevent, or somehow recover from, UDB
by silly checks such as comparing the address of the referenced object
to 0.
Suppose I write the following:

#include <iostream>

void f(int& foo)
{
if (&foo == 0)
std::cout << "&foo is zero\n";
}

int main()
{
int* p = 0;

f(*p);
return 0;
}

I think you are saying that if I compile and execute this program and it
prints "&foo is zero", that means that my compiler is broken. If so, I
think you are mistaken: I think that if you compile and execute this
program, you cannot draw any inference at all about whether the compiler is
working correctly, regardless of what the program does when executed.

Yes! You have illustrated much better what I was trying to say.
My reasoning is simple: p is a zero pointer, so the effect of evaluating *p
is undefined. So the moment the program has evaluated *p, the
implementation is free to do absoluately anything it wishes, and you cannot
claim it is broken regardless of what it does.

So if you are saying that only a broken compiler would print "&foo is zero"
when given the program above, I think you're mistaken. If you mean
something else, would you mind clarifying what it is?

I am sorry that I implied something which cannot be the compiler's
fault. It is solely the programmer's fault. I keep forgetting that no
diagnostic is necessary for pograms that cause UDB (although that
would be really nice! <g>).
 

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,787
Messages
2,569,629
Members
45,332
Latest member
LeesaButts

Latest Threads

Top