C99 Question

  • Thread starter Vijay Kumar R Zanvar
  • Start date
R

Richard Heathfield

E. Robert Tisdale said:
Do *you* have good reasons for doing so?

I'm not him, but yes I do. I write C programs for preference, but
occasionally use C++ for RAD of GUI programs. I don't see why I should
foresake the use of my familiar C libraries when writing C++ programs. But
I don't see why I should rewrite my C code to be C++-compilable either. So
I simply call my C code from my C++ code. It works fine.
How could you know that?

Because, believe it or not, most people aren't in the business of writing
libc implementations for which they ship the source.
My reasons are the same as Plauger's.

I'm afraid I find that very hard to believe, unless NASA has given up on
space altogether and decided to focus on the software tools development
side of things instead.
 
E

E. Robert Tisdale

Richard said:
I'm suggesting that, if you wish to use C code in a C++ program,
you can generally do it without recompiling the C code.

Really! Are you an expert in C/C++ interoperability now?

Tell us how you would do it
without compiling the C code with a C++ compiler.
Is your method guaranteed to work?
 
E

E. Robert Tisdale

Richard said:
I write C programs for preference
but occasionally use C++ for RAD of GUI programs.
I don't see why I should forsake the use of my familiar C libraries
when writing C++ programs.

Who said that you should?
But I don't see why I should rewrite my C code
to be C++-compilable either.

Who said that you should?
So I simply call my C code from my C++ code. It works fine.

Please cite and quote the passage from the ANSI/ISO C++ standard
that *guarantees* that "it works fine".

C functions compiled by one compiler need not be callable
from programs compiled with any other compiler --
not even another C compiler.
The only way to guarantee that you can call C functions
from a C++ program is to compile them with the *same* C++ compiler.
 
K

Keith Thompson

E. Robert Tisdale said:
The problem is that I can't get my C compiler to accept this.

Are you suggesting that
I should write and maintain two separate versions of function richard?
Just so that I can use one with C and the other with C++ programs?

<OT> extern "C" </OT>
 
R

Richard Heathfield

E. Robert Tisdale said:
Really! Are you an expert in C/C++ interoperability now?

Tell us how you would do it
without compiling the C code with a C++ compiler.

extern "C" {
#include "mychdr.h"
};

and link the appropriate object file.
Is your method guaranteed to work?

No; it is possible to generate pathological examples which won't work. This
has not caused me the slightest difficulty in the Real World.
 
R

Richard Heathfield

E. Robert Tisdale said:
The only way to guarantee that you can call C functions
from a C++ program is to compile them with the *same* C++ compiler.

That is no guarantee, since not all C functions *can* be compiled by a C++
compiler.
 
S

Sidney Cadot

Martin said:
I consider people who compile C code with a C++ compiler to be quite at
the newbie end of the spectrum.

Why? I'd be truly interested to know.

Best regards,

Sidney
 
S

Sidney Cadot

However you have, I believe, a different motive. You want your
shrouded source to be compilable by unwashed idiots on any
compiler without complaint or handholding.

This is intentionally phrased in a very negative way, of course. Putting
aside the negative connotations, what's wrong in wanting or needing this?

Best regards,

Sidney
 
A

Alexander Bartolich

begin followup to Sidney Cadot:
Why? I'd be truly interested to know.

Most IDEs use file extension alone to chose C or C++ compilation.
And since the default extension for new files is typically .cpp,
a lot of newbies end up feeding their supposed C code to a C++
implementation.

Sometimes it's hard to distinguish the clueless from the paranoid.
 
S

Sidney Cadot

Clark said:
[snip...]
Pick a language. Either write C or write C++. If you're writing C++,
then use new (and don't cast), if you're writing C, then use malloc (and
don't cast). If, you are writing C, but trying to feed it to a C++
compiler, then why not just write C++, and be through with it?

The alternative you suggest is often simply not possible. For example,
my current project involves a library that needs to link to Matlab and
IDL, which cannot reliably be made to work with C++ compiled code - they
only support C plugins.

As to the point about compiling C code with a C++ compiler. While it is
certainly arguable that the merits of feeding C code to a C++ are
dubious, one has to bear in mind that it is almost a free ride. Most
properly written C code compiles flawlessly in C++, except for a very
small number of cases. Lack of malloc() result casting is by far the
most prominent cause for failure you would normally encounter. Given the
widely shared belief that it is a good thing to throw your code at as
many compilers as you can get your hands on to improve portability, and
maximize the number of warnings you get, I personally think it is worth it.

Then there's the second point I made a couple of times, that I consider
it also proper in terms of elegance: the result of a
"malloc(1000*sizeof(double))" is properly thought of as double*, and
it's only the fact that malloc() must be a general function that forces
it to return a void*. Casting it to its proper type (double *) restores
this obvious anomaly - I think it's proper to do this ASAP.

Now I'm not advocating that we go through all this once again, but I
always get a bit vexed when I see the zealous feeding frenzy that ensues
when anyone dares to mention the malloc casting issue.

Often times, the people telling me to stop compiling my C code with a
C++ compilers ("better download a good lint that will do equally well or
better" - a good point in itself), all of the sudden toss out the stale
"you may fail to notice that you didn't include stdlib.h" when arguing
the malloc cast issue. To them I say:

- make up your mind as to whether you are discussing C89 or C99.
- download a lint, or a free compiler that helps you detect this.
If you use a compiler that doesn't complain on prototype
omission, you have more pressing issues to worry about.
- failure to use strict warning flags in your compiler (just about
the only way you can get hurt by malloc casting nowadays) is a
much more serious software engineering practice mistake than
casting a malloc() result could ever be, even if you don't accept
the advantages.

I guess what I am trying to say is that the issue is not as clear-cut as
many here make it out to be. It sometimes looks like rehashing of
arguments that were only relevant ten years ago.


Best regards, Sidney
 
S

Sidney Cadot

Alexander said:
begin followup to Sidney Cadot:


Most IDEs use file extension alone to chose C or C++ compilation.
And since the default extension for new files is typically .cpp,
a lot of newbies end up feeding their supposed C code to a C++
implementation.

Well I guess everybody here would agree that people who do this without
even being aware of it are rather clueless.

However, I'd hope to get a slightly more provoking answer from Martin
himself :)
Sometimes it's hard to distinguish the clueless from the paranoid.

I don't think it's hard at all, actually. Just ask why they're doing it.
If they fail to understand the question, it's settled :) If they give
some arguments that hold some ground, they're of the paranoid persuasion
(although, belonging in the latter category, I'd prefer 'cautious'
myself ;-)). The interesting part in that case lies in the reaction of
the asker. If he dismiss the argument out of hand, the discussion is for
all practical purposes over. Otherwise, both parties may learn a bit.

Best regards,
Sidney
 
C

CBFalconer

Sidney said:
This is intentionally phrased in a very negative way, of course.
Putting aside the negative connotations, what's wrong in wanting
or needing this?

Why do you consider it negative (serious question)? I
deliberately used the "I believe" phrase in order to specify
possible misconceptions on my part. There is nothing wrong in
wanting or needing "this", it is simply (I believe) a desirable
attribute for his product.
 
S

Sidney Cadot

Why do you consider it negative (serious question)?

Because of the negative wording ('unwashed idiots', 'handholding').

Personally, I don't see much wrong (from the 'customer' perspective) in
the requirement that I want to recompile the delivered code using either
a C or C++ compiler. I don't presume one would have to be particularly
lacking in the personal hygiene-department, much less be an idiot, to
want this.
I deliberately used the "I believe" phrase in order to specify
possible misconceptions on my part. There is nothing wrong in
wanting or needing "this", it is simply (I believe) a desirable
attribute for his product.

Okay. Then we probably agree that Mr. Plauger at least has a valid
reason for wanting to compile C code using a C++ compiler.

Best regards,

Sidney
 
C

Chris Torek

... Most properly written C code compiles flawlessly in C++,
except for a very small number of cases.

Either you have a different definition of "properly written" than
I do, or your experience differs quite a bit from mine. Using
gcc's "-x c++" to compile .c files as C++ code, I find that *most*
of the (C) code I deal with fails to compile -- yet, in most cases, it
seems "properly written" to me.
Lack of malloc() result casting is by far the most prominent
cause for failure you would normally encounter.

This is just one of many errors that turns up. But the diagnosed
errors are not the ones that really worry me. The scariest are
the silent changes in semantics:

% cat t.c
#include <stdio.h>

struct A { char x[10]; };
int main(void) {
struct B { struct A { int i; } b1, b2; };

printf("sizeof(struct A) = %lu\n", (unsigned long)sizeof(struct A));
return 0;
}
% cc -O -Wall -ansi -pedantic -o t t.c
% ./t
sizeof(struct A) = 4
% cc -x c++ -O -Wall -ansi -pedantic -o t t.c
% ./t
sizeof(struct A) = 10
%

(The above is just a boiled-down example, but we have had code like
it. The problem turned up when someone wanted to link C++ code
against an existing C library.)
I guess what I am trying to say is that the issue is not as clear-cut as
many here make it out to be. It sometimes looks like rehashing of
arguments that were only relevant ten years ago.

Well, C99 does obviate the "omit the cast to obtain a diagnostic
if you somehow forgot <stdlib.h>" argument. But if you are using
C99, you might start using some of the new C++ features, which are
not exactly compatible with C++. C99's bool and complex types are
different, its new "restrict" keyword does not exist in C++, its
anonymous aggregate constructor syntax means something new, and so
on.
 
C

Clark Cox

Sidney Cadot said:
Clark said:
[snip...]
Pick a language. Either write C or write C++. If you're writing C++,
then use new (and don't cast), if you're writing C, then use malloc
(and don't cast). If, you are writing C, but trying to feed it to a
C++ compiler, then why not just write C++, and be through with it?
The alternative you suggest is often simply not possible. For example,
my current project involves a library that needs to link to Matlab and
IDL, which cannot reliably be made to work with C++ compiled code - they
only support C plugins.

That's exactly what 'extern "C"' is for.
As to the point about compiling C code with a C++ compiler. While it is
certainly arguable that the merits of feeding C code to a C++ are
dubious, one has to bear in mind that it is almost a free ride. Most
properly written C code compiles flawlessly in C++, except for a very
small number of cases. Lack of malloc() result casting is by far the
most prominent cause for failure you would normally encounter. Given the
widely shared belief that it is a good thing to throw your code at as
many compilers as you can get your hands on to improve portability, and
maximize the number of warnings you get, I personally think it is worth it.

Then there's the second point I made a couple of times, that I consider
it also proper in terms of elegance: the result of a
"malloc(1000*sizeof(double))" is properly thought of as double*, and
it's only the fact that malloc() must be a general function that forces
it to return a void*. Casting it to its proper type (double *) restores
this obvious anomaly - I think it's proper to do this ASAP.

Of course, seeing as void* automatically converts to any other
pointer type, there is no need for the cast. The simple fact that you
always assign the result of malloc to some variable (in any non-trivial
program), and that variable (hopefully) is of the proper type, the cast
is completely superfluous. Most of the potential dangers have been
closed with the removal of implicit int in C99, so it is no longer such
a dangeous thing to do, but it is still a useless thing to do.

When I'm reviewing code, casting, in general, is a sign that
something out of the ordinary is going on.
Now I'm not advocating that we go through all this once again, but I
always get a bit vexed when I see the zealous feeding frenzy that ensues
when anyone dares to mention the malloc casting issue.

Often times, the people telling me to stop compiling my C code with a
C++ compilers ("better download a good lint that will do equally well or
better" - a good point in itself), all of the sudden toss out the stale
"you may fail to notice that you didn't include stdlib.h" when arguing
the malloc cast issue. To them I say:

- make up your mind as to whether you are discussing C89 or C99.
- download a lint, or a free compiler that helps you detect this.
If you use a compiler that doesn't complain on prototype
omission, you have more pressing issues to worry about.
- failure to use strict warning flags in your compiler (just about
the only way you can get hurt by malloc casting nowadays) is a
much more serious software engineering practice mistake than
casting a malloc() result could ever be, even if you don't accept
the advantages.

I guess what I am trying to say is that the issue is not as clear-cut as
many here make it out to be. It sometimes looks like rehashing of
arguments that were only relevant ten years ago.

Yes, with C99, there isn't much reason *not to* cast, but there is
still no reason *to* cast. And, if both doing something and not doing
something are equally meaningless, then I it makes logical sense to opt
for the "not doing something" choice.
 
S

Sidney Cadot

That's exactly what 'extern "C"' is for.

extern "C" is for letting a C++ compiler use C-type linkage, allowing
use of a C library from within C++ (but /not/ the other way round, which
would be needed for your suggestion to work).
[snip]
>> Then there's the second point I made a couple of times, that I consider
it also proper in terms of elegance: the result of a
"malloc(1000*sizeof(double))" is properly thought of as double*, and
it's only the fact that malloc() must be a general function that forces
it to return a void*. Casting it to its proper type (double *) restores
this obvious anomaly - I think it's proper to do this ASAP.

Of course, seeing as void* automatically converts to any other
pointer type, there is no need for the cast.

It does only do so on assignment, passing as a known-type parameter, and
perhaps a couple of other situations. You implicitly depend on any of
these things being done to the malloc() result, in order to have it
converted to its proper type. This is of course true in the vast
majority of cases.

Last time this came up I showed the following possible bug:

int *x = malloc(1000*sizeof(double));

.... not casting the malloc ASAP here makes the bug go unnoticed, i.e.,

int *x = (double *)malloc(1000*sizeof(double));

Would trigger a compile-time error.

Several responses were made then. The first one was "I don't see this
happen in practice" - which is dodging the issue.

Another one was, that this should properly be written as:

int *x = malloc(1000*sizeof *x);

....That's one way of avoiding the problem. However, what to in this case:

#define N 1000

int *fill(int *x, int n, int value)
{
int i;

if (!x)
exit(EXIT_FAILURE);

for(i=0;i<n;i++) x=value;

return x;
}

void do_something_clever(int *x, int n)
{
/*......*/
}

int main(void)
{
/* no way to get the sizeof of a formal parameter's base type.
* resort to giving the base type literally... */

do_something_clever(fill(malloc(N*sizeof(double), N, -1), N);

/*...*/
return 0;
}

Sure, this is not the world's most elegant code, but, in principle,
there's nothing wrong with it - other than the bug of course.

The point here is that relying on conversion-by-assignment for a
malloc() result is something that cannot be relied upon for 100% of
cases. It just happens to be the malloc() usage pattern in 99.99% of cases.
The simple fact that you always assign the result of malloc to
> some variable (in any non-trivial program),

Yes. That's an implicit dependence on a usage pattern, which is rarely
spelled out when the 'rule' is stated.
and that variable (hopefully) is of the proper type, the cast
is completely superfluous.

From a pragmatic standpoint, I agree. But I disagree on a more
fundamental level.
Most of the potential dangers have been
closed with the removal of implicit int in C99, so it is no longer such
a dangeous thing to do, but it is still a useless thing to do.

When I'm reviewing code, casting, in general, is a sign that
something out of the ordinary is going on.

I agree, but then I think that correcting the compiler's idea of a type
of a memory block qualifies.

I'm not trying to persuade you at all. What I am saying is that it is
not a completely black-or-white thing. A case can be made for the cast,
although you are free to think it's a very weak case of course.
Yes, with C99, there isn't much reason *not to* cast, but there is
still no reason *to* cast.

With C99 and any modern C89 compiler.
And, if both doing something and not doing
something are equally meaningless, then I it makes logical sense to opt
for the "not doing something" choice.

Unless (as I do) I want to compile the C code with a C++ compiler as
well. Most here will find this a bad reason though, and that's fine by me.

Best regards,

Sidney
 
S

Sidney Cadot

Either you have a different definition of "properly written" than
I do, or your experience differs quite a bit from mine. Using
gcc's "-x c++" to compile .c files as C++ code, I find that *most*
of the (C) code I deal with fails to compile -- yet, in most cases, it
seems "properly written" to me.

In your experience, what are the most important reasons for this to fail?
This is just one of many errors that turns up.
But the diagnosed errors are not the ones that really worry me.
> The scariest are the silent changes in semantics:
Certainly.

% cat t.c
#include <stdio.h>

struct A { char x[10]; };
int main(void) {
struct B { struct A { int i; } b1, b2; };

printf("sizeof(struct A) = %lu\n", (unsigned long)sizeof(struct A));
return 0;
}
% cc -O -Wall -ansi -pedantic -o t t.c
% ./t
sizeof(struct A) = 4
% cc -x c++ -O -Wall -ansi -pedantic -o t t.c
% ./t
sizeof(struct A) = 10
%

Ouch, that's messy.

In all objectivity though: wouldn't you agree that most C programmers
(even moderately able ones) would expect sizeof(struct A) to refer to
the first definition? I know I would.

To me, this example says: C has some pretty non-intuitive rules with
regard to type resolution; avoid shadowing type names to avoid confusion.

When I compile a C program as C++, I make a habit of also testing the
resulting executable (the fact that one can do this is an advantage
compared to lint). I would expect all my code to work in C++ as it does
in C. If this fails for some reason, I am bound to learn something from it.

In other words, I tend to constrain myself to the very large subset of C
that's also C++ (which is surprisingly easy). Extra constraints are a
good thing: that's why I prefer typed languages, am happy with C
prototypes, and use very strict warning flags. In my limited experience,
the C/C++-intersection language I use does not impose many strange
demands on my C coding style (the malloc-cast, in practice, is the only
one that I am consciously aware of), and it gives some benefits. The
mileage of most here does seem to be worse than mine, although I suspect
that some haven't even done the test drive :)
(The above is just a boiled-down example, but we have had code like
it. The problem turned up when someone wanted to link C++ code
against an existing C library.)

You must be glad you cought it. It's a nasty one.... It does not even
qualify as a bug in C (it has perfectly well-defined semantics), but
still it is best to avoid this type of thing altogether, I would say.

Now for one question that I hesitate to ask: would you have cought this
issue earlier on in your development cycle if you had done a C++ test
compile & run every couple of weeks or so? (Perhaps that's not possible
because your code is too "non-C++" for that to be an option).

I prefer to do that once in a while on a Friday afternoon; the number of
(potential) problems I catch in that way is small, but also non-zero.
Well, C99 does obviate the "omit the cast to obtain a diagnostic
if you somehow forgot <stdlib.h>" argument. But if you are using
C99, you might start using some of the new C++ features, which are
not exactly compatible with C++. C99's bool and complex types are
different, its new "restrict" keyword does not exist in C++, its
anonymous aggregate constructor syntax means something new, and so
on.

Yes, there will be some divergence (although I suppose C++ compilers
will start to support "restrict" sooner or later). For me, it's quite
simple: as long as the benefits of having the ability to compile C
programs with C++ programs outweigh the disadvantages, I'll keep on
doing it. For now, it's a small enough price to pay (IMHO).

Best regards,

Sidney
 
T

those who know me have no need of my name

in comp.lang.c i read:
When I compile a C program as C++, I make a habit of also testing the
resulting executable (the fact that one can do this is an advantage
compared to lint). I would expect all my code to work in C++ as it
does in C. If this fails for some reason, I am bound to learn
something from it.

once you find the issue. as chris showed there were no compiler detected
errors, and you can easily see how that sort of thing might only be found
later when trying to diagnose odd results -- thus depends entirely on the
quality of the test suite. further the `problem' only exists when using an
executable compiled with the `wrong' compiler, the program works as
expected otherwise.
 
S

Sidney Cadot

those said:
in comp.lang.c i read:
once you find the issue. as chris showed there were no compiler detected
errors, and you can easily see how that sort of thing might only be found
later when trying to diagnose odd results -- thus depends entirely on the
quality of the test suite. further the `problem' only exists when using an
executable compiled with the `wrong' compiler, the program works as
expected otherwise.

Well, that is all true. I'm not sure I get your point though.

Best regards,

Sidney
 
C

Clark Cox

Sidney Cadot said:
extern "C" is for letting a C++ compiler use C-type linkage, allowing
use of a C library from within C++ (but /not/ the other way round, which
would be needed for your suggestion to work).

[mildly-OT]
extern "C" *does* work in both directions (i.e. you can write C++
code that is callable from C code and vice versa):

//C++ code in file file1.cpp:
extern "C"
{
int foo(int i)
{
return i+1;
}
}

/*C code in file file2.c:*/
#include <stdio.h>

extern int foo(int);

int main()
{
printf("%d\n", foo(1));
return 0;
}
[/mildly-OT]
 

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
474,135
Messages
2,570,784
Members
47,342
Latest member
KelseyK737

Latest Threads

Top