stdbool.h


I

Ian Collins

Tim said:
I think you have missed the point of what I've been saying. In
any case I cannot be responsible for your lack of imagination.

David was one of many who couldn't imagine any "perfectly reasonable
local uses" and requested an example. I'm adding my name to the list of
those lacking imagination! Please enlighten us.
 
Ad

Advertisements

M

Malcolm McLean

Tim Rentsch wrote:

David was one of many who couldn't imagine any "perfectly reasonable
local uses" and requested an example. I'm adding my name to the list of
those lacking imagination! Please enlighten us.
#include "fantasticllycomplexexternallib.h"
/* Note: don't touch this library, it's cleverer that you are !!!"

int foo(char *program)
{
int err;
bool halts;

err = solvehaltingproblem(program, &halts);
if(err)
return -1;
else return halts ? 1 : 0;

}
 
D

David Brown

David Brown said:
On 10/03/14 01:10, Keith Thompson wrote: [...]
Use of all-caps *for macros* is still a good idea, because of the
potential they have to do violence to the language syntax and semantics.
Consider, for example:

#define MAX(x, y) ( (x) >= (y) ? (x) : (y) )

The all-caps name makes it clear that something odd could be going on --
in this case, that one of the arguments will be evaluated twice.

There is a simple answer here - don't write code that has side effects
inside function arguments (such as MAX(x++, y++)). Also, avoid writing
general function-like macros that might cause trouble if people /do/
call them with side-effect arguments. With modern C, static inline
functions can replace many function-like macros with a clearer and safer
alternative. And if you really need generic handling of different
types, there are several possibilities (gcc "typeof", C11 _Generic, C++
templates) - although these all have obvious disadvantages.

That's fine if the author of the macro definition and the author of
any code that uses it are the same person. I don't particularly
like side effects in function arguments, but it's not practical
to ban them -- and sometimes something like func(count++) is more
convenient than the alternative. An all-caps macro name is a
reminder to *avoid* arguments side effects.

The trouble is, all-caps is at best a vague suggestion that the
"function" is likely to be a macro. It is not a guarantee - in
particular, just because an identifier does not have all-caps does not
mean that it is a function and not a macro. So using all-caps for the
macros does not actually benefit the user significantly, but it still
has a cost (in terms of ugly code, unwanted emphasis, and difficulty in
reading).
If you're writing a header to be used by other programmers, and you
feel the need to define macros in that header, it's probably a good
idea to follow the conventions those other programmers are likely
to understand. (And yes, that's a slightly more sophisticated
version of "do it that way because I said so".)

I agree on that. When working on other people's code, or working with
other people, it is sometimes necessary to compromise on what you know
yourself to be good style - consistency, local conventions, and
cooperation can trump the rules you prefer when you have complete
control. And for this reason I will occasionally use all-caps for
macros - but it is not by choice.
Static inline functions can't replace MAX and MIN, which work for any
type with a ">=" operator. gcc's "typeof" is obviously not portable,
and template are not C. I suppose you could use _Generic in a macro
definition along with a collection of N functions to implement max
and min for N different types, but I don't think it would handle
arguments of different types.

I haven't made use of it myself, but I believe _Generic can be nested to
handle the types of more than one parameter. And I believe (again, this
is untested) that you could make a limited gcc "typeof" operator using
_Generic:

#define integer_typeof(x) _Generic((x), \
_Bool : _Bool, unsigned char : unsigned char, \
signed char : signed char, short : short, \
unsigned short : unsigned short, int : int, \
unsigned int : unsigned int, long : long, \
unsigned long : unsigned long, long long : long long, \
unsigned long long : unsigned long long, \
default : int)

Still, it's going to be a bit messy - all right for a library header,
but not something you'd want to write in the application code.

In any case, if you're using something other than macros, the
all-caps convention isn't even relevant.

That's kind of my point. Functions that used to be implemented as
macros are now often made as static inline functions (with the exception
of type-generic "max" functions, and similar). Only the implementation
has changed, not the use - it makes no sense to change the naming
convention because of the implementation style. Ergo, function-like
macros should have been named like functions. Similarly for constant
macros.
Most but not all. The name of a "static const int" object isn't a
constant expression, and an enumeration constant can only be of type
int.

Yes - and these limitations are quite annoying. In particular (in my
code at least), it is a pain that you cannot use a "static const int"
for the size of an array - even though the compiler knows it as a
compile-time constant and optimises its use just as if it were a literal
number.
If I see a reference to "max_number_of_items" without seeing its
definition, I'm likely to assume that it's a variable whose value might
be determined at execution time. If I see MAX_NUMBER_OF_ITEMS, I'll
assume it's a compile-time constant. The use of all-caps does convey
useful information to the reader. (Which admittedly doesn't cover the
enum case; I never claimed any of this was 100% consistent.)

I'll certainly agree that if I see the name in all-caps, I'll assume it
is constant - but if I see it in small letters and it is relevant to
know if it is constant or variable (which is not something that happens
often), I would check its definition.
If I have a piece of code that has a MAX() macro, and I replace the
macro definition with a static inline function, should I go through all
the code and change "MAX(..)" to "max(..)" just because the macro has
turned into a function - even though it does exactly the same thing, and
generates exactly the same code? Conversely, if I want to change an
existing function into a macro do I then need to rename everything in
all-caps just to follow this convention?

If you want to follow the convention strictly, yes. But it's only a
convention, and you don't have to follow it in your own code if you
don't want to.
I understand that many people use this convention, and I understand that
people think it gives them some sort of warning or protection against
mistakes with macros. I just don't agree with it - at least, not in
/my/ code. (And I know my view here is controversial.)
[...]
The obvious way to introduce a boolean type to the language would have
been the C++ approach, making bool, false, and true all keywords. But
that would have broken existing code. The existing solution (adding
_Bool as a built-in type, and bool, false, and true as macros in
<stdbool.h>) is probably about the best that could have been done given
that constraint. (Though I would have made bool a typedef rather than a
macro.)

I too would have had bool as a typedef - and false and true as
enumeration constants rather than macros (but as you noted above, bool
itself cannot be an enum). Perhaps the language could have defined
_True and _False as keywords for the type, equivalent to C++'s keywords,
and then put macros in <stdbool.h> to define true and false as _True and
_False rather than 1 and 0. But then the standards people might have
been tempted to say that the values of _True and _False, when converted
to integers, were implementation defined....

Making false and true (however they're defined) be of type _Bool/bool
would have been clean, but given C's rather, um, promiscuous implicit
conversions it wouldn't have made any difference 99% of the time.
Making them have type int is consistent with the treatment of both
character constants and enumeration constants.

I don't think there would have been any temptation to make the results
of conversions implementation-defined. Conversions of _Bool values to
integer types are well defined anyway -- and in fact _Bool *is* an
integer type.
 
D

David Brown

Different developers have different opinions on that point, but
in any case it's irrelevant to what I was saying.

I agree that different developers have different opinions here - I am
just offering mine. And it is a little relevant, in that you are
suggesting using TRUE and FALSE because you think "true" and "false"
look like they could be variable.
Nonsense, as James Kuyper has pointed out.

One can keep habits from old systems after new ones are introduced -
such as having a habit of writing some things in all-caps even after you
are able to use small letters. However, it is a very minor issue - I
mean to say that while all-caps macro names might have made sense long
ago, I do not like it in modern programming.
That has no bearing on my comment.


Are you always so parochial? Is it so hard for you to imagine
that other developers might have different opinions on these
topics?

I think it is pretty clear that when I express an opinion, it is my
opinion - unless you think I have some sort of official status as a
representative for many other programmers. And yes, I fully understand
that opinions vary one topics like this - I am giving you /my/ opinion
and some justification for it. Feel free to ignore it, or give your own
opinions. I certainly value hearing about other ideas, or other
justifications for existing ideas, even if I don't agree with them myself.
The header <stdbool.h> is part of a standard implementation, but
use of <stdbool.h> is optional, not mandatory. Moreover, the
Standard gives explicit and specific permission to undefine the
macros bool, true, and false from <stdbool.h>, so obviously the
Standard anticipates that some developers would want to do that
for one or more of those symbols, and perhaps define their own
versions of these identifiers, even after <stdbool.h> has been
included as a header.

The standard always has to consider compatibility with old code, or with
odd code. <stdbool.h> is optional, and has undefineable macros, in
order to let people work with old code that defines its own bool type
and constants. But it /allows/ this sort of thing - it certainly does
not /recommend/ or /suggest it/. In fact, C11 explicitly says "The
ability to undefine and perhaps then redefine the macros bool, true, and
false is an obsolescent feature" in the section on "Future library
directions".

What I take from this is that you can mess around with your own
definitions of bool, true and false if you have to - but you should not
do so when working on modern and future-proof code.

You're engaging in hyperbole.

Tell that to people who have to maintain code with TRUE (or "true")
defined to 0 or -1, or other such joys. People /have/ written such
code, and telling them that it's a good idea to invent your own versions
of standard types and standard identifiers is a bad idea. The standard
libraries and standard headers are there for a reason, and they should
be used unless you have a very particular reason to do something else
(such as for working with old code).
What I am doing is pointing out
alternate definitions for bool, true, and false, with somewhat
different characteristics for how the identifiers can be used,
that may be preferable in some circumstances. If some
developers find that their own circumstances favor those
different characteristics, they may reasonably prefer to use
those alternate definitions; in other circumstances they may
reasonably prefer to use the <stdbool.h> definitions. The
choice is up to them. Under some circumstances (which I will
not describe), I would advocate using using the <stdbool.h>
definitions; in others I would advocate using the alternate
definitions mentioned above. IMO this question should not be
given a "one size fits all" kind of answer.

I understand what you are saying here. But what we are missing is any
good reasons as to when one would want to use the definitions you
suggest rather than <stdbool.h>. There are occasions why one might need
an odd definition (such as for weird code that defines TRUE to be 0),
but I fail to see any serious advantage of your definition over the
standard one. It's not that I think there is anything /wrong/ with your
enum type - it is just that I see no reason to use it rather than the
standard one.

The idea that bool might or should be an enumerated type has
no bearing on my comments.


I think you have missed the point of what I've been saying. In
any case I cannot be responsible for your lack of imagination.

You /are/ responsible in this case for telling us that there are
"perfectly reasonable" cases where the <stdbool.h> macros would cause
conflicts but your typedef enum version would work, and for telling us
that there are cases where the enum version is significantly better than
the <stdbool.h> version. So either I - and several other posters here -
am lacking imagination, or you are making up justification for your
preferred implementation of bool. Of course, it is absolutely fine for
you to prefer your own implementation - but I would very much like to
hear something more concrete about uses where it makes a real
difference. Unfortunately, with every post in which you fail to give
examples, you re-enforce the thought that you don't have any good
examples. Please prove me wrong here!
 
K

Keith Thompson

David Brown said:
David Brown said:
On 10/03/14 01:10, Keith Thompson wrote: [...]
Use of all-caps *for macros* is still a good idea, because of the
potential they have to do violence to the language syntax and semantics.
Consider, for example:

#define MAX(x, y) ( (x) >= (y) ? (x) : (y) )

The all-caps name makes it clear that something odd could be going on --
in this case, that one of the arguments will be evaluated twice.

There is a simple answer here - don't write code that has side effects
inside function arguments (such as MAX(x++, y++)). Also, avoid writing
general function-like macros that might cause trouble if people /do/
call them with side-effect arguments. With modern C, static inline
functions can replace many function-like macros with a clearer and safer
alternative. And if you really need generic handling of different
types, there are several possibilities (gcc "typeof", C11 _Generic, C++
templates) - although these all have obvious disadvantages.

That's fine if the author of the macro definition and the author of
any code that uses it are the same person. I don't particularly
like side effects in function arguments, but it's not practical
to ban them -- and sometimes something like func(count++) is more
convenient than the alternative. An all-caps macro name is a
reminder to *avoid* arguments side effects.

The trouble is, all-caps is at best a vague suggestion that the
"function" is likely to be a macro. It is not a guarantee - in
particular, just because an identifier does not have all-caps does not
mean that it is a function and not a macro. So using all-caps for the
macros does not actually benefit the user significantly, but it still
has a cost (in terms of ugly code, unwanted emphasis, and difficulty in
reading).

By that reasoning, calling a function "print_foo" is at best a
vague suggestion that it prints a foo. But unless the author is
deliberately perverse, the name provides extremely useful information
about what the function does.

I, and a lot of other programmers, find the convention of using
all-caps names for macros, useful. It's not pretty, but then this is C
we're talking about. :cool:}

[...]
I haven't made use of it myself, but I believe _Generic can be nested to
handle the types of more than one parameter.

Perhaps -- but I think the size of the code would be proportional to the
square of the number of types it handles, and it couldn't be portably
written to apply to extended integer types. Perhaps it could be
generated automatically.
And I believe (again, this
is untested) that you could make a limited gcc "typeof" operator using
_Generic:

#define integer_typeof(x) _Generic((x), \
_Bool : _Bool, unsigned char : unsigned char, \
signed char : signed char, short : short, \
unsigned short : unsigned short, int : int, \
unsigned int : unsigned int, long : long, \
unsigned long : unsigned long, long long : long long, \
unsigned long long : unsigned long long, \
default : int)

Still, it's going to be a bit messy - all right for a library header,
but not something you'd want to write in the application code.

You'd want plain char as well (which is distinct from both unsigned char
and signed char).
That's kind of my point. Functions that used to be implemented as
macros are now often made as static inline functions (with the exception
of type-generic "max" functions, and similar). Only the implementation
has changed, not the use - it makes no sense to change the naming
convention because of the implementation style. Ergo, function-like
macros should have been named like functions. Similarly for constant
macros.

I'm certainly not arguing that there are no problems with the
convention. In my opinion, the benefits outweigh the problems.
I'm not a big fan of ALL-CAPS in general -- I don't even have
a working caps-lock key on my keyboard -- but for macro names it
doesn't particularly bother me for some reason. If I found all-caps
particularly jarring, I suspect I'd agree with you.

[...]
 
B

Ben Bacarisse

David Brown said:
[...] And I believe (again, this
is untested) that you could make a limited gcc "typeof" operator using
_Generic:

#define integer_typeof(x) _Generic((x), \
_Bool : _Bool, unsigned char : unsigned char, \
signed char : signed char, short : short, \
unsigned short : unsigned short, int : int, \
unsigned int : unsigned int, long : long, \
unsigned long : unsigned long, long long : long long, \
unsigned long long : unsigned long long, \
default : int)

In a generic expression, what follows the ':' must itself be an
expression (specifically the syntactic production 'assignment-
expression') so I don't see how this can work.

<snip>
 
Ad

Advertisements

M

Malcolm McLean

That's kind of my point. Functions that used to be implemented as
macros are now often made as static inline functions (with the exception
of type-generic "max" functions, and similar). Only the implementation
has changed, not the use - it makes no sense to change the naming
convention because of the implementation style. Ergo, function-like
macros should have been named like functions. Similarly for constant
macros.
The trivial functions you need

uniform(); - return a uniform random number on 0 - 1-epsilon.
min / maxN - minimum/maximium of 2, 3, 4 and so on numbers.
lerp(a, b, t) - linear interpolation between a and b on t = 0-1.
clamp(x, low, high) - force value into range low - high.
sign(x) - 0 1 or -1

Generally it makes sense to implement as macros.
 
D

David Brown

David Brown said:
On 10/03/14 01:10, Keith Thompson wrote:
[...]
Use of all-caps *for macros* is still a good idea, because of the
potential they have to do violence to the language syntax and semantics.
Consider, for example:

#define MAX(x, y) ( (x) >= (y) ? (x) : (y) )

The all-caps name makes it clear that something odd could be going on --
in this case, that one of the arguments will be evaluated twice.

There is a simple answer here - don't write code that has side effects
inside function arguments (such as MAX(x++, y++)). Also, avoid writing
general function-like macros that might cause trouble if people /do/
call them with side-effect arguments. With modern C, static inline
functions can replace many function-like macros with a clearer and safer
alternative. And if you really need generic handling of different
types, there are several possibilities (gcc "typeof", C11 _Generic, C++
templates) - although these all have obvious disadvantages.

That's fine if the author of the macro definition and the author of
any code that uses it are the same person. I don't particularly
like side effects in function arguments, but it's not practical
to ban them -- and sometimes something like func(count++) is more
convenient than the alternative. An all-caps macro name is a
reminder to *avoid* arguments side effects.

The trouble is, all-caps is at best a vague suggestion that the
"function" is likely to be a macro. It is not a guarantee - in
particular, just because an identifier does not have all-caps does not
mean that it is a function and not a macro. So using all-caps for the
macros does not actually benefit the user significantly, but it still
has a cost (in terms of ugly code, unwanted emphasis, and difficulty in
reading).

By that reasoning, calling a function "print_foo" is at best a
vague suggestion that it prints a foo. But unless the author is
deliberately perverse, the name provides extremely useful information
about what the function does.

I see your point, but I don't quite agree with it here. It is true that
an all-caps "function" will be a macro, assuming a reasonable author -
but it is perfectly reasonable for a non-caps "function" to be a macro.
Indeed, the standard headers are full of macros that are not in
all-caps. And it is the knowledge that you are dealing with a real
function, and not a function-like macro, that lets you (safely) have
side effects in your arguments - information that you cannot get from
the name alone.

One compromise could be to use all-caps for macros which re-use their
parameters, and are therefore unsafe for side-effects. Thus:

#define MAX(x, y) ( (x) >= (y) ? (x) : (y) )
#define sum(x, y) ((x) + (y))

That would give you the extra information you want from all-caps when
you need it, while keeping code neater when you don't need it.
I, and a lot of other programmers, find the convention of using
all-caps names for macros, useful. It's not pretty, but then this is C
we're talking about. :cool:}

I put the balance point of ugliness vs. usefulness at a different point
in the case of all-caps macros. But I understand other people see it
differently - maybe I am just lucky in that because of the way I work, I
can choose to avoid all-caps macro names.
[...]
I haven't made use of it myself, but I believe _Generic can be nested to
handle the types of more than one parameter.

Perhaps -- but I think the size of the code would be proportional to the
square of the number of types it handles, and it couldn't be portably
written to apply to extended integer types. Perhaps it could be
generated automatically.

Extended integer types are non-portable anyway.

But I think you are right that the size of source code would be O(n^2) -
although the generated code should be optimal.
You'd want plain char as well (which is distinct from both unsigned char
and signed char).

Maybe, although I don't think it would make a difference here. It
depends on what you want to do with such a "typeof" macro - and on how
_Generic handles chars. For a max() macro, I think it would be
sufficient to handle "int" and above - integer promotion would mean that
a max(char, char) would be handled like max(int, int) anyway.
I'm certainly not arguing that there are no problems with the
convention. In my opinion, the benefits outweigh the problems.
I'm not a big fan of ALL-CAPS in general -- I don't even have
a working caps-lock key on my keyboard -- but for macro names it
doesn't particularly bother me for some reason. If I found all-caps
particularly jarring, I suspect I'd agree with you.

And if I found all-caps particularly useful, then I'd agree with you :)

It's just a matter of where we draw the line, and that's partly personal
opinion, and partly the type of code we deal with (for most of my
programming, I am the only one who works with the code - and that makes
a huge difference to how much I can make the rules, and how much I
follow other people's rules or conventions).
 
D

David Brown

David Brown said:
[...] And I believe (again, this
is untested) that you could make a limited gcc "typeof" operator using
_Generic:

#define integer_typeof(x) _Generic((x), \
_Bool : _Bool, unsigned char : unsigned char, \
signed char : signed char, short : short, \
unsigned short : unsigned short, int : int, \
unsigned int : unsigned int, long : long, \
unsigned long : unsigned long, long long : long long, \
unsigned long long : unsigned long long, \
default : int)

In a generic expression, what follows the ':' must itself be an
expression (specifically the syntactic production 'assignment-
expression') so I don't see how this can work.

I didn't know that rule - I thought _Generic was more like macro
expansion. Of course that changes things, and means that such a
typeof() _Generic could not work. Thanks for point it out.
 
B

Ben Bacarisse

David Brown said:
David Brown said:
[...] And I believe (again, this
is untested) that you could make a limited gcc "typeof" operator using
_Generic:

#define integer_typeof(x) _Generic((x), \
_Bool : _Bool, unsigned char : unsigned char, \
signed char : signed char, short : short, \
unsigned short : unsigned short, int : int, \
unsigned int : unsigned int, long : long, \
unsigned long : unsigned long, long long : long long, \
unsigned long long : unsigned long long, \
default : int)

In a generic expression, what follows the ':' must itself be an
expression (specifically the syntactic production 'assignment-
expression') so I don't see how this can work.

I didn't know that rule - I thought _Generic was more like macro
expansion.

It's just a form of expression so as far as the abstract machine goes
it's a run-time construct. Of course, since the choice is based on
information that is available at compile time, no sane implementation
will leave any trace of it in the executable. In that sense it *is*
like macro expansion.

<snip>
 
J

James Kuyper

Extended integer types are non-portable anyway.

But code that is portable can make use of extended integer types, if
they come into play through standard typedefs such as size_t, int64_t,
sig_atomic_t, etc. Dealing with this possibility using _Generic() is
problematic, because "No two generic associations in the same generic
selection shall specify compatible types.".
 
Ad

Advertisements

K

Keith Thompson

David Brown said:
Maybe, although I don't think it would make a difference here. It
depends on what you want to do with such a "typeof" macro - and on how
_Generic handles chars. For a max() macro, I think it would be
sufficient to handle "int" and above - integer promotion would mean that
a max(char, char) would be handled like max(int, int) anyway.

_Generic uses the unpromoted type of the controlling expression;
promotion happens only for operators that explicitly say their
operands are promoted. For example:

#include <stdio.h>
int main(void) {
char c;
puts(_Generic(c, char: "char",
signed char: "signed char",
unsigned char: "unsigned char",
int: "int",
default: "default"));
}

The output is "char" (using clang 3.4).
 
K

Keith Thompson

Ben Bacarisse said:
It's just a form of expression so as far as the abstract machine goes
it's a run-time construct. Of course, since the choice is based on
information that is available at compile time, no sane implementation
will leave any trace of it in the executable. In that sense it *is*
like macro expansion.

Based on the description in the C11 standard, I don't think it's a
run-time construct even in the abstract machine.

N1570 6.5.1.1p3:

The controlling expression of a generic selection is not
evaluated. If a generic selection has a generic association with
a type name that is compatible with the type of the controlling
expression, then the result expression of the generic selection
is the expression in that generic association. Otherwise, the
result expression of the generic selection is the expression in
the default generic association. None of the expressions from any
other generic association of the generic selection is evaluated.

Note that this talks about the result *expression*, not the result
*value*.
 
B

Ben Bacarisse

Keith Thompson said:
Based on the description in the C11 standard, I don't think it's a
run-time construct even in the abstract machine.

N1570 6.5.1.1p3:

The controlling expression of a generic selection is not
evaluated. If a generic selection has a generic association with
a type name that is compatible with the type of the controlling
expression, then the result expression of the generic selection
is the expression in that generic association. Otherwise, the
result expression of the generic selection is the expression in
the default generic association. None of the expressions from any
other generic association of the generic selection is evaluated.

Note that this talks about the result *expression*, not the result
*value*.

Ah, yes. I had thought that variably modified types would be permitted,
but they are excluded explicitly in the preceding paragraph and would be
useless anyway some the controlling expression is not evaluated.
 
E

Eric Sosman


I don't. The article is about C++ rather than C, and many of
the drawbacks of the various approaches appear to be self-inflicted
by the choice of language:

- The "Option 1" problem doesn't even arise in C.

- "Option 2" works the same way in C++ and C, but the only
objection is pure opinion. The opinion may make sense for
C++ (I "disavow any knowledge"), but doesn't seem especially
telling in C terms.

- "Option 3" apparently works better in C than in C++, due to
a C++ restriction C doesn't suffer from.

- "Option 4" is simply not in the cards for C, not at all.

So: While the cases for and against a built-in boolean type
in C may be plausibly argued, this article doesn't do so. It's
about as relevant to C as a discussion of boolean types in COBOL.
 
Ad

Advertisements

K

Keith Thompson

Anand Hariharan said:

Only partly. The linked article (by Herb Sutter) is about whether bool
could have been emulated in C++ without addingit as a built-in type. In
C, overloading is not an issue, and conditions are not converted to
bool/_Bool anyway.

C99's addition of _Bool does two things that couldn't have been done
with a user-defined type: it standardizes a single Boolean type for all
implementations, and it ensures conversions to _Bool can only yield
false or true (0 or 1). The former could have been accomplished by
adding <stdbool.h> with something like:

typedef enum { false, true } bool;

but the latter required a language change.
 
D

David Brown

But code that is portable can make use of extended integer types, if
they come into play through standard typedefs such as size_t, int64_t,
sig_atomic_t, etc. Dealing with this possibility using _Generic() is
problematic, because "No two generic associations in the same generic
selection shall specify compatible types.".

OK, that clause makes it quite a lot harder. Perhaps the best way then
would be to include <stdint.h> and use types intN_t for _Generic -
the different sizes (assuming the platform supports them, of course)
will not be compatible with each other.

Are different standard integer types "compatible" if they are the same
size? i.e., if both "int" and "long int" are 32-bit, are they
considered "compatible" for _Generic? I couldn't find a clear statement
in the C11 standard.


I guess _Generic will have its uses - but replacing gcc's very nice
"typeof" operator is not one of them!
 
D

David Brown

_Generic uses the unpromoted type of the controlling expression;
promotion happens only for operators that explicitly say their
operands are promoted. For example:

#include <stdio.h>
int main(void) {
char c;
puts(_Generic(c, char: "char",
signed char: "signed char",
unsigned char: "unsigned char",
int: "int",
default: "default"));
}

The output is "char" (using clang 3.4).

Yes, I understand that - but the implementation of "max" is going to use
a comparison operator which will lead to a promotion at that point.
 
Ad

Advertisements

J

James Kuyper

But code that is portable can make use of extended integer types, if
they come into play through standard typedefs such as size_t, int64_t,
sig_atomic_t, etc. Dealing with this possibility using _Generic() is
problematic, because "No two generic associations in the same generic
selection shall specify compatible types.".

OK, that clause makes it quite a lot harder. Perhaps the best way then
would be to include <stdint.h> and use types intN_t for _Generic -
the different sizes (assuming the platform supports them, of course)
will not be compatible with each other.

Are different standard integer types "compatible" if they are the same
size? ...


As a practical matter (I'll get to issue of what the standard says
farther down), same size is insufficient to enable two types to be
compatible. They must also have the same representation and alignment
requirements, and must use the same mechanism when passed as a parameter
and when returned as the value of a function.
... i.e., if both "int" and "long int" are 32-bit, are they
considered "compatible" for _Generic? I couldn't find a clear statement
in the C11 standard.

I've argued in the past that the standard guarantees compatibility for
certain pairs of types (which do not include "int" and "long"), but that
it does not prohibit other pairs of types from happening to be
compatible on particular implementations. I was told that the cases
where the standard specifies that two types are compatible are
exhaustive - no other pairs of types can be compatible. This is an
example of a more general rule when interpreting the standard: whenever
it provides a list of things for which something is true, that list is
exhaustive unless the standard explicitly says otherwise. I knew of that
rule, but find that argument less than compelling, because the
compatibility rules are not provided as a list, but as several
independent clauses in widely separated parts of the standard.
However, the official interpretation is that whenever a constraint
requires that two types be compatible, it is a violation of that
constraint, requiring a diagnostic, if the two types are not ones
explicitly specified as being compatible by the standard.

After issuing the required diagnostic, the implementation is free to
produce an executable anyway, and if you chose to execute the resulting
program it might behave in exactly the same fashion as would be
mandatory if the standard had specified that those two types were
compatible, and the implementation is free to document this as a fact.
However, the diagnostic message is still the only mandatory result of
translating such a program. The implementation's documentation cannot,
therefore, accurately describe this fact by saying "int and long int are
compatible"; it must use some other wording to describe that fact.
 

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

Top