Saturday, October 23, 2021

Digest for comp.lang.c++@googlegroups.com - 12 updates in 1 topic

David Brown <david.brown@hesbynett.no>: Oct 23 11:54AM +0200

On 22/10/2021 17:14, Bart wrote:
 
> With mine, it displays:
 
>   int
>   const int
 
It turns out - who would have guessed? - that gcc is correct. The gcc
developers these days tend to be very careful and strict about this kind
of thing.
 
As Keith says, the controlling expression undergoes "lvalue conversion"
(this is in 6.5.1.1p2, if you want to look it up). C18 helpfully adds a
footnote that did not exist in C11, saying "An lvalue conversion drops
type qualifiers". (I think the standard could benefit from more of such
explanatory footnotes.)
 
I think it is odd, however, that you can have qualified types in the
generic association list, since they can't ever match anything (AFAICS).
David Brown <david.brown@hesbynett.no>: Oct 23 12:21PM +0200

On 22/10/2021 16:48, Bart wrote:
>> restricts you, thus making code clearer, safer, more maintainable, and
>> perhaps sometimes more efficient.
 
> There are better ways of doing it.
 
There are certainly /different/ ways of doing things. There are lots of
different programming languages, with their different strengths and
weaknesses.
 
> In C, it is just adds a lot of
> clutter that effects readability and can hide real problems.
 
What an odd idea.
 
If you don't like "const", don't use it in your programming. Others
find it useful to aid readability and avoid problems.
 
 
> Neither does the syntax make it that obvious which bit of the type is
> refered to, as in:
 
>   const int * const * x;
 
If you find this kind of thing confusing, use "typedef". It exists to
improve readability (amongst other benefits).
 
> a const pointer to a struct which contains non-const pointers. The
> 'const' only protects that top level; it does not stop you writing
> nested non-const data.
 
You mean, people who don't really understand what they are doing and
write code that confuses themselves, get mixed up? And how is C
different from any other language in that respect?
 
I appreciate that you personally prefer a different ordering when
writing types, and that you are not alone in that. Fine. C has a
different ordering, and people usually manage perfectly well. The
difference between "const int * x", "int * const x" and "const int *
const x" is one of these things newbies to C often find hard, and it
turns up in every FAQ and tutorial on the language. If /you/ still find
it hard, read a FAQ.
 
> In my example, x can still be written to! (x=0 is allowed; but *x=0 and
> **x=0 are not.)
 
Yes - x is not const.
 
Bart <bc@freeuk.com>: Oct 23 11:22AM +0100

On 23/10/2021 10:54, David Brown wrote:
> explanatory footnotes.)
 
> I think it is odd, however, that you can have qualified types in the
> generic association list, since they can't ever match anything (AFAICS).
 
They can be used in examples like this:
 
#include <stdio.h>
 
#define strtypeof(t) _Generic(t,\
const int*: "pointer to const int",\
int*: "pointer to int",\
default: "other")
 
int main(void) {
int * p;
const int * q;
 
puts(strtypeof(p));
puts(strtypeof(q));
}
 
All compilers that support _Generic show:
 
pointer to int
pointer to const int
 
This suggests a way to maintain those top level qualifiers, by wrapping
a pointer around a type. But it would be an ungainly workaround (and the
fact that typeof() also drops those qualifiers would might make it
impractical).
David Brown <david.brown@hesbynett.no>: Oct 23 12:35PM +0200

On 23/10/2021 12:22, Bart wrote:
 
>       puts(strtypeof(p));
>       puts(strtypeof(q));
>   }
 
Yes - but those are not qualified types. Using your preferred ordering,
a "pointer to int" and "pointer to const int" are different types.
 
> a pointer around a type. But it would be an ungainly workaround (and the
> fact that typeof() also drops those qualifiers would might make it
> impractical).
 
Why are you inventing an ugly workaround for a non-existent problem?
_Generic in C looks at the unqualified type of an expression - there
isn't a problem.
 
It turns out your compiler has a bug due to a slight misunderstanding of
_Generic. I'm glad you've found it, and can correct it (assuming you
want to be closer to following the standards). But no one is looking
for a "workaround" here. (Especially not /here/, in c.l.c++ !)
Bart <bc@freeuk.com>: Oct 23 12:06PM +0100

On 23/10/2021 11:21, David Brown wrote:
 
> What an odd idea.
 
> If you don't like "const", don't use it in your programming. Others
> find it useful to aid readability and avoid problems.
 
I was thinking more about other people's code. Mine doesn't use const at
all.
 
 
>>   const int * const * x;
 
> If you find this kind of thing confusing, use "typedef". It exists to
> improve readability (amongst other benefits).
 
So, even /more/ clutter?! I's also like to see a typedefed version of my
example that is not harder to understand.
 
 
> You mean, people who don't really understand what they are doing and
> write code that confuses themselves, get mixed up? And how is C
> different from any other language in that respect?
 
Yes, everybody. You have a dynamic tree data structure for example,
using non-const references within its nodes to allow it to be updated.
 
How do you write a function that takes a reference to that tree, but is
not allowed to update it?
 
This is what someone might expect of an immutable parameter.
 
This is not to say that I know how to achieve this; I don't (but I
haven't researched it much either). The nearest I can do is pass a deep
copy of such a tree, to protect the original, but that is hardly efficient.
 
I just see C's const as a waste of time. I'm starting to use readonly
data in a few places without my languages, but where it's handled sensibly.
 
What I don't do is introduce such a polarising type attribute at every
level of a data structure, one that poisons every other type it comes
into contact with, such that it becomes challenging to do perfectly
innocuous things.
Ben Bacarisse <ben.usenet@bsb.me.uk>: Oct 23 01:23PM +0100


> I think it is odd, however, that you can have qualified types in the
> generic association list, since they can't ever match anything
> (AFAICS).
 
I was curious so I tried this:
 
#include <stdio.h>

struct S { const int i; } s;
struct S f(void) { return s; }

int main(void)
{
const char *t =
_Generic(f().i,
int: "int",
const int: "const int");
puts(t);
}
 
f().i is not a lvalue and has a const-qualified type. gcc prints "int",
but clang prints "const int". I think clang is right here. (So much
for "they all copy gcc"!)
 
--
Ben.
Bart <bc@freeuk.com>: Oct 23 01:57PM +0100

On 23/10/2021 13:23, Ben Bacarisse wrote:
 
> f().i is not a lvalue and has a const-qualified type. gcc prints "int",
> but clang prints "const int". I think clang is right here. (So much
> for "they all copy gcc"!)
 
You need to file a bug report to Clang's developers so that they can fix
that oversight!
 
But, why do think Clang is wrong? The type has a top-level const qualifier.
 
I don't get why this is only removed for an lvalue (where you'd think
that a const attribute is more critical).
David Brown <david.brown@hesbynett.no>: Oct 23 06:45PM +0200

On 23/10/2021 13:06, Bart wrote:
>> find it useful to aid readability and avoid problems.
 
> I was thinking more about other people's code. Mine doesn't use const at
> all.
 
Perhaps if you used more of C's common features yourself, you'd be less
confused about them and less inclined to think they are "clutter" or
hinder readability. (If you only want to use C as an output language
from your transpilers, and thus only use a subset of the language, then
that's absolutely fine - but it makes you a poor judge of what features
are useful to people working with human-written C code rather than
machine-generated C code.)
 
>> improve readability (amongst other benefits).
 
> So, even /more/ clutter?! I's also like to see a typedefed version of my
> example that is not harder to understand.
 
typedef const int constant_integer;
typedef constant_integer * pointer_to_constant_integer;
typedef const pointer_to_constant_integer
constant_pointer_to_constant_integer;
typedef constant_pointer_to_constant_integer *
pointer_to_constant_pointer_to_constant_integer;
 
pointer_to_constant_pointer_to_constant_integer x;
 
 
That's the order you prefer, is it not? (I'm not suggesting it's a good
way to write it, I'm merely showing you how it could be done with a
choice of names that might suit your liking.)
 
 
Maybe you want it more compact:
 
typedef const int * p_cint;
typedef const p_cint * p_cp_cint;
p_cp_cint x;
 
 
In real code, of course, it would usually make more sense to think about
what your types actually are and how they will be used, and then use
type names that fit.
 
 
 
> How do you write a function that takes a reference to that tree, but is
> not allowed to update it?
 
> This is what someone might expect of an immutable parameter.
 
There are occasions when it is more convenient to cast away const, or
where it is hard to maintain full const correctness. "const" does not
absolve the programmer of having to think. But it does make a lot of
code clearer and easier to understand.
 
> level of a data structure, one that poisons every other type it comes
> into contact with, such that it becomes challenging to do perfectly
> innocuous things.
 
Nobody does that with "const". I guess it is just yet another of C's
features that you don't quite understand, and prefer to hate
irrationally than learn.
Bart <bc@freeuk.com>: Oct 23 06:58PM +0100

On 23/10/2021 17:45, David Brown wrote:
 
> typedef const int * p_cint;
> typedef const p_cint * p_cp_cint;
> p_cp_cint x;
 
 
Well, I was right, the alternatives are worse.
 
If you are interested in the actual type, or the 'shape' of that type,
devoid of qualifiers, then you don't want all that. You want to know the
type is 'int**'.
 
 
> where it is hard to maintain full const correctness. "const" does not
> absolve the programmer of having to think. But it does make a lot of
> code clearer and easier to understand.
 
Somebody writes an informal library but doesn't bother to mark with
'const' those functions that take char* that don't happen to modify the
string:
 
void f1(char*);
void f2(char*);
void f3(char*);
 
Now someone who has a mania for 'const' wants to use it:
 
const char* s="ABC";
f1(s);
 
However, it doesn't work. They will know from the specs that f1 doesn't
write into the string, but the compiler doesn't know that.
 
Now, it starts to get messy. Either casts have to be inserted, or the
library needs to be heavily revised. Then that library may import
another which is also missing consts. And so const-poisoning infects the
whole code-base.
 
At some point, it will also stop you doing things legally, and you have
to start using casts. Now, you are starting to fight the language.
 
Was it Pascal or Ada that first had those in/out parameter attributes?
 
I can write this [in my syntax]:
 
proc f1(ichar s) = {} # anything goes
proc f2(ichar in s) = {} # s is input to the function
proc f3(ichar out s) = {} # s is output from the function
 
I don't do anything with these at the minute (I think 'out' and 'inout',
not shown, are just aliases for '&') but they can do a lot just as
annotations.
 
At some point an implementation can enforce them and ensure that an 'in'
data structure is not modified in the function, even one that has
mutable components. The programmer doesn't need to micro-manage every
level of the type structure, or have to think about exactly how
foolproof those 'const' attributes are.
 
It should be like a write-protect switch on the whole caboodle.
 
(At least, within the bounds of what the language can help with. A data
structure may contain references to external data, such as files, disks,
images, which can all be modifible, or they can be altered via another
path to the original data.
 
But C's const doesn't prevent that either.)
Ben Bacarisse <ben.usenet@bsb.me.uk>: Oct 23 09:02PM +0100

> fix that oversight!
 
> But, why do think Clang is wrong? The type has a top-level const
> qualifier.
 
I said I think clang is right (because the expression f().i is not an
lvalue).
 
> I don't get why this is only removed for an lvalue (where you'd think
> that a const attribute is more critical).
 
lvalue conversion converts an lvalue to the value stored. It makes no
sense for the result to have any qualifiers -- they are anything but
critical for pure values.
 
--
Ben.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Oct 23 09:30PM +0100

On Fri, 22 Oct 2021 04:41:47 -0000 (UTC)
> So yes, 'const' can actually make the program more efficient (especially
> in C++, where it guarantees to the compiler that it can assume the
> contents won't change).
 
Since this thread is entitled "I think references should have been
const by default", it may be worth mentioning that holding a const
reference to an object does not mean that the compiler "can assume the
contents won't change". It guarantees that, in the absence of a const
cast, non-mutable non-static data won't be modified through the
reference. If the object concerned is a lvalue it says nothing about
what might be done to the object's non-mutable data through its
variable name (assuming that is non-const) or by some other non-const
reference. It also says nothing about the mutability of the object's
static data (if any).
 
I say this in case it is used to put forward the incorrect notion that
"const" means "thread safe", which I have occasionally seen propagated
by the ill-informed.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Oct 23 03:07PM -0700


> But, why do think Clang is wrong? The type has a top-level const qualifier.
 
> I don't get why this is only removed for an lvalue (where you'd think
> that a const attribute is more critical).
 
The removal of type qualifiers is part of lvalue conversion. No lvalue,
no lvalue conversion.
 
I can see that it would make sense for the expression `f().i` to have
type int rather than const int, but the standard doesn't say so.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: