Unexpected behaviour

R

Roal Zanazzi

Hi everyone,

I've accidentally found something in my C++ code that is definitively weird.
It is obviously an error in my code, but it is not signalled by
Microsoft Visual C++ 2005 compiler, not even with a warning.
While Borland TurboC++ 2006 compiler stop at it as an error
(E2034 Cannot convert 'bool' to 'int *').


// Code start here //
#include <iostream>

void func(int* p1, bool p2 = false)
{
std::cout << p1 << ", " << p2 << std::endl;
}

int main(int argc, char* argv[])
{
func(false); // <<< PROBLEM!
return 0;
}
// Code end here //


What I was doing is trying to pass the second parameter explicitely
(even though it has the same value as the default one),
forgetting the first parameter (think about someone else changed the
function signature without telling you).

I'm curious about what the standard says about this situation.
Do the standard mandate an implicit cast from false to 0 (zero)?
Should the compiler issue an error, at least a warning or let it go?

If I replace the "offending line" with:
func(true); // <<< PROBLEM!
in this case also the Microsoft compiler issues an error
(C2664: 'func' : cannot convert parameter 1 from 'bool' to 'int *'),
the same one given by Borland compiler.

TIA
 
D

David Harmon

On Tue, 12 Sep 2006 13:04:20 +0200 in comp.lang.c++, Roal Zanazzi
If I replace the "offending line" with:
func(true); // <<< PROBLEM!

The definition of a null pointer is "An integral constant
expression rvalue of integer type that evaluates to zero".
bool is an integer type. false evaluates to zero. true does not.
 
R

Roal Zanazzi

David Harmon ha scritto:
On Tue, 12 Sep 2006 13:04:20 +0200 in comp.lang.c++, Roal Zanazzi


The definition of a null pointer is "An integral constant
expression rvalue of integer type that evaluates to zero".
bool is an integer type. false evaluates to zero. true does not.
This makes sense, somewhat.
Like saying that apples and oranges are different until you have none of
both :p

So false could be used instead of 0 (zero) or NULL... mmmh...

Anyway I see this as a potential cause of problems, what if I write:

// code start here //
#include <iostream>

void func(int* p1, bool p2 = true)
{
std::cout << p1 << ", " << p2 << std::endl;
}

int main(int argc, char* argv[])
{
func(false); // <<< not really passing p2!!!
return 0;
}
// code end here //

....and I think I'm passing a p2 param value different from the default
one, because I'm not aware of the presence of p1 param?
In this case I get no warning, BUT the program will not perform as expected.
This seems a lack of information, I would expect a warning here, or am I
completely wrong?

N.B. I'm not stating that the C++ standard is wrong (I'm not even near
to that level of knowledge), but that the compiler (Visual C++ 2005)
leaves a hole for potential bugs.
Borland C++ 2006 compiler seems to be non-compliant giving error.
g++ (GCC) 4.1.1 20060525 (Red Hat 4.1.1-1) gives no warning.

I'm curious, what are the results with other compilers (sorry, I have
only those 3)?
 
C

Clark S. Cox III

Roal said:
Hi everyone,

I've accidentally found something in my C++ code that is definitively
weird.
It is obviously an error in my code, but it is not signalled by

Obvious to me and you, but not to the compiler; the compiler is required
to accept what you've written below.
Microsoft Visual C++ 2005 compiler, not even with a warning.
While Borland TurboC++ 2006 compiler stop at it as an error
(E2034 Cannot convert 'bool' to 'int *').

TurboC++ is wrong in this case.
// Code start here //
#include <iostream>

void func(int* p1, bool p2 = false)
{
std::cout << p1 << ", " << p2 << std::endl;
}

int main(int argc, char* argv[])
{
func(false); // <<< PROBLEM!

false is a valid Null Pointer Constant; It is a constant integer
expression that evaluates to zero. This function call is no different
than any of the following:

func(0);
func(0U);
func(0L);
func(0UL);
func('\0');
func(1-1);
func(25 / 100);
func(sizeof(char) / 2);

or any of the other infinite variations on the same theme.
If I replace the "offending line" with:
func(true); // <<< PROBLEM!

(true) on the other hand is not a valid Null Pointer Constant (it does
not evaluate to zero).
 
M

Michiel.Salters

Clark said:
Obvious to me and you, but not to the compiler; the compiler is required
to accept what you've written below.

It must accept the code, but it may still give you a warning. Using
false
as a NPC is probably more often a mistake than intentional. The tricky
bit
for compiler writers is recognizing the ((int*) false) cases where you
try to
make the intent explicit.

Regards,
Michiel Salters.
 
H

Howard

Roal Zanazzi said:
David Harmon ha scritto:
On Tue, 12 Sep 2006 13:04:20 +0200 in comp.lang.c++, Roal Zanazzi


The definition of a null pointer is "An integral constant
expression rvalue of integer type that evaluates to zero".
bool is an integer type. false evaluates to zero. true does not.
This makes sense, somewhat.
Like saying that apples and oranges are different until you have none of
both :p

So false could be used instead of 0 (zero) or NULL... mmmh...

Anyway I see this as a potential cause of problems, what if I write:

// code start here //
#include <iostream>

void func(int* p1, bool p2 = true)
{
std::cout << p1 << ", " << p2 << std::endl;
}

int main(int argc, char* argv[])
{
func(false); // <<< not really passing p2!!!
return 0;
}
// code end here //

...and I think I'm passing a p2 param value different from the default
one, because I'm not aware of the presence of p1 param?

You can't leave off the first parameter, but include the second, ever. You
could leave off the second one, since it's got a default value, but not the
other way around.

And it's perfectly legal to pass a constant integer value as a pointer.
(That's why NULL is valid, after all.) There's no way the compiler could
"guess" that, simply because false is usually used as a boolean, you must
have somehow forgotten the first parameter, and meant that to be the second
one. (Can you imagine trying to write the code to decide that that was the
case?)


I don't see how this could ever be a real problem, though. How could you
not be aware of the existence of a first parameter, but at the same time
know the proper type of the second parameter?

-Howard
 
P

Puppet_Sock

Roal Zanazzi wrote:
[snips]
// Code start here //
#include <iostream>

void func(int* p1, bool p2 = false)
{
std::cout << p1 << ", " << p2 << std::endl;
}

int main(int argc, char* argv[])
{
func(false); // <<< PROBLEM!
return 0;
}
// Code end here //

This looks like a job for a lint type program. There are a few.
Have a google around.

A "nice" compiler would certainly hold your hand over such
and issue. Maybe issue a warning along the lines of "suspicious
conversion" or something.
Socks
 
R

Roal Zanazzi

Howard ha scritto:
Roal Zanazzi said:
David Harmon ha scritto:
func(true); // <<< PROBLEM!
The definition of a null pointer is "An integral constant
expression rvalue of integer type that evaluates to zero".
bool is an integer type. false evaluates to zero. true does not.
This makes sense, somewhat.
Like saying that apples and oranges are different until you have none of
both :p

So false could be used instead of 0 (zero) or NULL... mmmh...

Anyway I see this as a potential cause of problems, what if I write:

// code start here //
#include <iostream>

void func(int* p1, bool p2 = true)
{
std::cout << p1 << ", " << p2 << std::endl;
}

int main(int argc, char* argv[])
{
func(false); // <<< not really passing p2!!!
return 0;
}
// code end here //

...and I think I'm passing a p2 param value different from the default
one, because I'm not aware of the presence of p1 param?

You can't leave off the first parameter, but include the second, ever. You
could leave off the second one, since it's got a default value, but not the
other way around.
Sorry Howard, but it is exactly what I've done in the code above:
I presumed that the function func() was just taking 1 parameter of type
bool, with default value of true (and I've passed false to _change the
function behaviour_). I presumed this because the function signature was
like that, in the past.
Think about this in a more complex scenario where the function func()
comes from a library developed by someone else, and this person changes
the function signature adding 1 parameter (a pointer).
A "standard compliant" compiler will continue to compile without even
giving a small warning, and (in the worst-but-not-rare case) your
application will eventually blow up (at test time if you have been
smart, in your customer's eye if not).
And it's perfectly legal to pass a constant integer value as a pointer.
(That's why NULL is valid, after all.) There's no way the compiler could
"guess" that, simply because false is usually used as a boolean, you must
have somehow forgotten the first parameter, and meant that to be the second
one. (Can you imagine trying to write the code to decide that that was the
case?)
Any compiler CAN (and should, IMHO) issue a warning in this case, giving
the developer at least a minimal alarm about something that could be an
unwanted mistake.
Just like an assignment in a if() condition...
I don't see how this could ever be a real problem, though. How could you
not be aware of the existence of a first parameter, but at the same time
know the proper type of the second parameter?
Sh*t happens...
See above.

Maybe I'm just going pedantic, but I've yet to read a good reason for
this lack of warning.
 
H

Howard

Roal Zanazzi said:
Howard ha scritto:
Any compiler CAN (and should, IMHO) issue a warning in this case, giving
the developer at least a minimal alarm about something that could be an
unwanted mistake.
Just like an assignment in a if() condition...

Well, a warning could be issued for the use of the constant false where a
pointer is expected. That would be easy, and maybe even useful. (You might
want to discuss this on comp.std.c++.)

But what I was saying was that it wasn't practical for the compiler to
somehow know you were passing the second parameter, and accidently leaving
out the first.

Imagine if, instead of having an int* and a bool parameter, you had two bool
parameters, and then made the same mistake as you did, passing false for the
second one, not knowing that a new bool parameter had been added before it.
Now, would you still expect a warning? Obviously not, since false is a
valid bool value for the first parameter, and leaving off the second
parameter is allowed because of the default value.

That's what I was really getting at.

But, the situation you described should never have occured, really. Someone
modified a function and didn't make absolutely sure that everyone who might
call that function knew that a change had been made.

Which is why a good practice (at least in my opinion) is to NEVER change a
function's signature. Instead, add new functions which do the work you now
need done, and deprecate the old function. (One way to do that is to throw
an exception if the old function is called. There are other ways, as well.
But most importantly, whatever you do, document the change!) This is how
things are done in the COM/DCOM world. An interface is allowed to grow by
adding new functions, but changes are not allowed to existing function
signatures.

-Howard
 
R

Roal Zanazzi

Howard wrote
Well, a warning could be issued for the use of the constant false where a
pointer is expected. That would be easy, and maybe even useful. (You might
want to discuss this on comp.std.c++.)
Maybe I'm not the right person, my lack of deep knowledge in the
standard is not a good background for such an highly technical group...
But what I was saying was that it wasn't practical for the compiler to
somehow know you were passing the second parameter, and accidently leaving
out the first.
Ok, I get your point, anyway mine was not that the compiler should wear
the fortune-teller hat and read my mind ;-) It surely (hopefully) cannot
do it.
Imagine if, instead of having an int* and a bool parameter, you had two bool
parameters, and then made the same mistake as you did, passing false for the
second one, not knowing that a new bool parameter had been added before it.
Now, would you still expect a warning? Obviously not, since false is a
valid bool value for the first parameter, and leaving off the second
parameter is allowed because of the default value.

That's what I was really getting at.
Understood and perfectly logic. I obviously agree with you.
But I see my point as different, because it applies to a different and
well defined case: the compiler (can and) should issue a warning
whenever it finds a _bool to pointer implicit conversion_.
Correct me if I'm wrong here, but I think that this situation could be
helpful most of the times (if not all) to prevent potentially weird bugs.

But, the situation you described should never have occured, really. Someone
modified a function and didn't make absolutely sure that everyone who might
call that function knew that a change had been made.
Believe me, I'm not the one that knows nothing about SE at al. :p
Which is why a good practice (at least in my opinion) is to NEVER change a
function's signature. Instead, add new functions which do the work you now
need done, and deprecate the old function.
>
I tend to stay away from NEVER and ALWAYS.
By I agree with you about it being a _good practice_.
(One way to do that is to throw
an exception if the old function is called. There are other ways, as well.
But most importantly, whatever you do, document the change!) This is how
things are done in the COM/DCOM world. An interface is allowed to grow by
adding new functions, but changes are not allowed to existing function
signatures.
I know, I know... we have some ActiveX control (sorry about the OT)
which are growing full of *Ex() functions that add some parameter over
the *() original functions...
In this case the function I'm calling has no public visibility (just
used inside a DLL library).
 

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

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top