Function factories

J

James Kuyper

Yes. It compiles with a warning as expected. Not a great example of type
safety.

It's a lot better than it would be if no warning were mandated. An
implementation is free to reject such code - whether or not it actually
does so is a matter of QoI, outside the scope of the standard.
This code on the other hand compiles with no warnings:

#include <stdlib.h>

int main(void)
{
int *ptr = (int*) malloc(sizeof(long));
*ptr = 10;
short blah = *ptr;
free(ptr);
exit(EXIT_SUCCESS);
}

so please explain how C is a type safe language?

Type safety is not an absolute yes or no issue. Some languages have more
type safety than others. C has static type checking - in many different
contexts, a diagnostic is mandatory if different types are not
compatible. Implicit conversions are often safe conversions, the most
dangerous type conversions are required to be explicit (except those
pointer conversions which can be done using void* as an intermediate
type). There's plenty of room for improvement in C's type safety, but
it's still a lot better than having no type checking, or deferring type
checking until run-time (both of which I've seen in other languages).
The fact that malloc() returns a void pointer that can be cast to any
type destroys any type safety C may have.

No, it doesn't. It just adds another type-unsafe mechanism to a language
that has only a moderate amount of type safety without it.

lcc-win32's compile() looks to me like it compromises type safety much
more than malloc() does, which is what prompted my original comment. A
pointer to a function that might point have the wrong function type
seems much more serious to me than a pointer to an object that might
have the wrong object type; there's a larger variety of ways you can
have a mis-match, and a correspondingly larger number of different
failure modes available. It's not good, either way.
 
J

jacob navia

Le 23/08/12 19:35, James Kuyper a écrit :
lcc-win32's compile() looks to me like it compromises type safety much
more than malloc() does, which is what prompted my original comment. A
pointer to a function that might point have the wrong function type
seems much more serious to me than a pointer to an object that might
have the wrong object type; there's a larger variety of ways you can
have a mis-match, and a correspondingly larger number of different
failure modes available. It's not good, either way.

But how can "compile()" know what type of function you are going to define?

There is absolutely NO WAY to change this fact. But of course if you
have one solution please let me know...
 
J

James Kuyper

gcc by default compiles just fine, but issues warning ;)
g++ gives error and that I don;t like ...

Personally, I like it when a compiler lets me know I've written
dangerously bad code. Why do you feel differently?
 
M

Melzzzzz

Personally, I like it when a compiler lets me know I've written
dangerously bad code. Why do you feel differently?

I wasn't clear. c++ issues error and that I don;t like,
while behavior of C compiler is just fine IMO ;)
 
J

James Kuyper

I wasn't clear. c++ issues error and that I don;t like,
while behavior of C compiler is just fine IMO ;)

No, you were quite clear, but perhaps my response wasn't. It seems to me
that treating such code as an error is entirely appropriate; a warning
seems inadequate. Why do you feel differently?

If you actually have a legitimate need to do the conversion, and know
something implementation-specific that makes is safer than it is in the
general case, make those facts explicit by using a cast (in C) or a
reinterpret_cast<> (in C++). Requiring something as clumsy and as easily
searched for as reinterpret_cast<> in order to perform such a dangerous
conversion also strikes me as a good idea.
 
M

Melzzzzz

No, you were quite clear, but perhaps my response wasn't. It seems to
me that treating such code as an error is entirely appropriate; a
warning seems inadequate. Why do you feel differently?

It seems that reduces number of casts in code. Warning is enough
if it is accidental error.
If you actually have a legitimate need to do the conversion, and know
something implementation-specific that makes is safer than it is in
the general case, make those facts explicit by using a cast (in C) or
a reinterpret_cast<> (in C++). Requiring something as clumsy and as
easily searched for as reinterpret_cast<> in order to perform such a
dangerous conversion also strikes me as a good idea.

Well compiler warnings are enough aren't they? Cast just masks warning
and makes compiler happy which is not good thing.
Well if you have to search trough code to find that one error is much
more difficult rather then just check compiler warning.
I don;t like to shut up compiler warnings with casts and that's
why C's approach is better.
 
J

James Kuyper

It seems that reduces number of casts in code. Warning is enough
if it is accidental error.

As opposed to a deliberate error?
I have to disagree with you about whether reducing the number of casts
is a good idea. See below.
Well compiler warnings are enough aren't they? Cast just masks warning
and makes compiler happy which is not good thing.

No, in the unlikely event that you have legitimate reason to write such
code, a cast explicitly requests that the conversion should occur (and
in some cases makes a conversion occur that could not occur implicitly).
It documents the developer's assertion that there is a legitimate reason
for performing what is ordinarily a rather dangerous operation. Code
that is correctly written to do what it does should not generate
warnings, even if it does use dangerous methods to get there. The danger
should be marked by the presence of the cast, not by generation of
warning messages. Producing warning messages from correct code
desensitizes developers to warning messages associated with incorrect
code. The cast should be easy to notice and search for, which is one
reason why reinterpret-cast said:
Well if you have to search trough code to find that one error is much
more difficult rather then just check compiler warning.

That doesn't explain why you think a compiler error is worse than a
compiler warning. If the error message is written properly, both will
give you the location of the problem, so there's no need to go searching.
I don;t like to shut up compiler warnings with casts and that's
why C's approach is better.

C doesn't make any different approach than C++ in this case; the
construct is equally an error in both languages, and neither language
specifies what an implementation should do about it, beyond issuing the
mandatory diagnostic. It's the compiler that's decided to handle these
two languages differently, not the language specifications.

Note that, given C's anti-aliasing rules, in the unlikely event that you
do have a legitimate reason to write code like that, it would be better
to use code like this:

union { double d, int i} u;
int *pi = &u.i;
 
K

Keith Thompson

Melzzzzz said:
Well compiler warnings are enough aren't they? Cast just masks warning
and makes compiler happy which is not good thing.
Well if you have to search trough code to find that one error is much
more difficult rather then just check compiler warning.
I don;t like to shut up compiler warnings with casts and that's
why C's approach is better.

The code in question was:

double d;
int *pi = &d;

and the question is whether a compiler should reject it with an error
message or accept it with a mere warning.

Personally, I would never deliberately write that. As far as the C
standard is concerned, it's a constraint violation, requiring a
diagnostic from any conforming compiler. If a compiler issues a warning
and continues to compile the code, it might seem reasonable to assume
that it will generate a conversion from `double*` to `int*` -- but in
fact the language standard does not say or imply that that's what will
happen. The behavior is undefined; a compiler could legally generate
code that sets `pi` to a null pointer, for example.

Whereas if I *want* to do that conversion, there's a perfectly valid way
to do it that likely won't result in a warning:

double d;
int *pi = (int*)&d;

A problem I have with gcc is that it doesn't clearly distinguish between
warnings about constraint violations, like the above, and warnings about
legal but questionable code, such as writing `if (x = 42)` rather than
`if (x == 42)`. Again, this doesn't violate the C standard; I just
personally find it annoying.

That's just my opinion (that all syntax errors and constraint violations
should be treated as fatal errors, not as warnings -- or at least that
there should be an option that works that way). What's yours? Which
errors do you think should be fatal, and which should merely trigger a
warning?
 
M

Melzzzzz

The code in question was:

double d;
int *pi = &d;

and the question is whether a compiler should reject it with an error
message or accept it with a mere warning.

Personally, I would never deliberately write that. As far as the C
standard is concerned, it's a constraint violation, requiring a
diagnostic from any conforming compiler. If a compiler issues a
warning and continues to compile the code, it might seem reasonable
to assume that it will generate a conversion from `double*` to `int*`
-- but in fact the language standard does not say or imply that
that's what will happen. The behavior is undefined; a compiler could
legally generate code that sets `pi` to a null pointer, for example.

I didn't knew that this is undefined behavior.
Whereas if I *want* to do that conversion, there's a perfectly valid
way to do it that likely won't result in a warning:

double d;
int *pi = (int*)&d;

Problem is that we wont get warning in this case IMO.
A problem I have with gcc is that it doesn't clearly distinguish
between warnings about constraint violations, like the above, and
warnings about legal but questionable code, such as writing `if (x =
42)` rather than `if (x == 42)`. Again, this doesn't violate the C
standard; I just personally find it annoying.

That's just my opinion (that all syntax errors and constraint
violations should be treated as fatal errors, not as warnings -- or
at least that there should be an option that works that way). What's
yours? Which errors do you think should be fatal, and which should
merely trigger a warning?

I don't think that converting one pointer type to other is error.
It seems to me that in spirit of C it should be allowed as it is.
It should issue error if types are incompatible, say pointer to int
has less bytes than pointer to double.
Problem is that if code that uses such conversions will work at all.
Say copying data as int / reading as double.
It might work in one case and might not in other.
It would be very difficult to find out bug (wrong result etc) without
compiler warning IMO.
 
K

Keith Thompson

Melzzzzz said:
I didn't knew that this is undefined behavior.


Problem is that we wont get warning in this case IMO.

A compiler *could* issue a warning (because the conversion is
potentially unsafe), but most don't. A cast, particularly a pointer
cast, is effectively a way of telling the compiler "Shut up, I know what
I'm doing".

Are you saying you'd *want* a warning for the cast?
I don't think that converting one pointer type to other is error.

Converting from one pointer type to another using a cast is not an
error, though the result is implementation-defined, and using the
converted pointer can lead to undefined behavior.

*Assigning* a pointer value of one type to an object of another
incompatible pointer type is a constraint violation (apart from
special cases involving void*). Likewise for initialization and
argument passing.
It seems to me that in spirit of C it should be allowed as it is.
It should issue error if types are incompatible, say pointer to int
has less bytes than pointer to double.

The word "incompatible" has a specific meaning in C, and it's not
related to the sizes of the types. For example, `int*` and `long*` are
incompatible, even if `int` and `long` happen to have exactly the same
size and representation.
Problem is that if code that uses such conversions will work at all.
Say copying data as int / reading as double.
It might work in one case and might not in other.
It would be very difficult to find out bug (wrong result etc) without
compiler warning IMO.

I'm still not sure what you're saying. A compiler *must* issue a
diagnostic for an assignment between incompatible pointer types.
In my opinion, that diagnostic *should* be a fatal error.

Are you saying that a mere warning is preferable? Why?
 
E

eq mail

Yes. It compiles with a warning as expected. Not a great example of type
safety.

That's up to your implementation. The fact that you do get a
diagnostic tells that a level of type-safety is there, but you should
probably look for settings that would make such diagnostics also fatal
errors.
This code on the other hand compiles with no warnings:

#include <stdlib.h>

int main(void)
{
        int *ptr = (int*) malloc(sizeof(long));
        *ptr = 10;
        short blah = *ptr;
        free(ptr);
        exit(EXIT_SUCCESS);

}

so please explain how C is a type safe language?

I don't think this specific example succeeds at showing the
deficiencies of C and it's type system nearly as much as it highlights
several bad coding practices and a pitfall of one standard library
function.
 
E

eq mail

Le 23/08/12 19:02, Chicken McNuggets a crit :




Because you can assign a 64 bit integer into a short.

Is that really an explanation for how C is a type safe language?
If there is an overflow the behavior is undefined
but it can't be any overflow in your example since
10 will fit into a short.

Integer conversions, such as the one above, do not yield undefined
behaviour.
 
E

Eric Sosman

Is that really an explanation for how C is a type safe language?


Integer conversions, such as the one above, do not yield undefined
behaviour.

The program is (technically) at risk of undefined behavior
on any machine where sizeof(int) > sizeof(long). No such machine
has ever been rumored, but would be within the realm of possibility
for a C implementation: int might be 40 bits wide (including 15
padding bits) while long is 32 with no padding.

Also, there's the possibility that malloc() returns NULL.

But turning to the conversion: Yes, it's well-defined -- But
that's only because the value 10 is within both the range of int
and the range of short. Change the 10 to 123456789101112 and you
get quite a different picture.
 
E

eq mail

     The program is (technically) at risk of undefined behavior
on any machine where sizeof(int) > sizeof(long).  No such machine
has ever been rumored, but would be within the realm of possibility
for a C implementation: int might be 40 bits wide (including 15
padding bits) while long is 32 with no padding.

     Also, there's the possibility that malloc() returns NULL.

This is a strange reply. I chose not to dissect the example (although
I mentioned bad coding practices in general else-thread).
     But turning to the conversion: Yes, it's well-defined -- But
that's only because the value 10 is within both the range of int
and the range of short.  Change the 10 to 123456789101112 and you
get quite a different picture.

Yes and no. It won't be UB no matter what, so the statement still
stands.
 
N

Nick Keighley

Melzzzzz, isn't the term "dangerously bad code" unclear in some way?
It seems that reduces number of casts in code. Warning is enough
if it is accidental error.

since when was "reduce[ing] number of casts" a programming goal?
Reducing *unnecessary* casts, yes
Well compiler warnings are enough aren't they?

my personnel (and company) programming standards require a warning
free compile. System test won't start testing code until it compiles
without warnings (for some Microsoft errors this actually means
suppressing some warnigns).
 
M

Melzzzzz

Melzzzzz said:
[...]
Well compiler warnings are enough aren't they? Cast just masks
warning and makes compiler happy which is not good thing.
Well if you have to search trough code to find that one error is
much more difficult rather then just check compiler warning.
I don;t like to shut up compiler warnings with casts and that's
why C's approach is better.

The code in question was:

double d;
int *pi = &d;

and the question is whether a compiler should reject it with an
error message or accept it with a mere warning.

Personally, I would never deliberately write that. As far as the C
standard is concerned, it's a constraint violation, requiring a
diagnostic from any conforming compiler. If a compiler issues a
warning and continues to compile the code, it might seem reasonable
to assume that it will generate a conversion from `double*` to
`int*` -- but in fact the language standard does not say or imply
that that's what will happen. The behavior is undefined; a
compiler could legally generate code that sets `pi` to a null
pointer, for example.

I didn't knew that this is undefined behavior.
Whereas if I *want* to do that conversion, there's a perfectly
valid way to do it that likely won't result in a warning:

double d;
int *pi = (int*)&d;

Problem is that we wont get warning in this case IMO.

A compiler *could* issue a warning (because the conversion is
potentially unsafe), but most don't. A cast, particularly a pointer
cast, is effectively a way of telling the compiler "Shut up, I know
what I'm doing".

Are you saying you'd *want* a warning for the cast?

Well, warning would be welcome , yes ;)
I'm still not sure what you're saying. A compiler *must* issue a
diagnostic for an assignment between incompatible pointer types.
In my opinion, that diagnostic *should* be a fatal error.

Are you saying that a mere warning is preferable? Why?

I think that forcing to cast from one type to another
is just too verbose, but I'm not sure anymore since
Im now changing my mind. All in all you two (James)
convinced me that I am wrong...
 
A

Andrew Cooper

Yes. It compiles with a warning as expected. Not a great example of type
safety.

This code on the other hand compiles with no warnings:

#include <stdlib.h>

int main(void)
{
int *ptr = (int*) malloc(sizeof(long));
*ptr = 10;
short blah = *ptr;
free(ptr);
exit(EXIT_SUCCESS);
}

so please explain how C is a type safe language?

C is not a strongly typed language.

Data is raw.
The numbers you find in RAM are just integers.

C will help you with types in 90% of cases, but the remaining 10% is
left up to the developer. This is the price of having complete and
utter control over anything you need if you wish (with the option of
degrading to assembly if you absolutely need to).

~Andrew
 

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
474,077
Messages
2,570,569
Members
47,206
Latest member
MalorieSte

Latest Threads

Top