Tuesday, November 20, 2018

Digest for comp.lang.c++@googlegroups.com - 25 updates in 3 topics

Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 20 12:47AM

On Mon, 19 Nov 2018 14:55:51 +0100
> parameter itself (when not using references) is rarely helpful - it does
> not affect the calling function at all, but makes the types harder to
> read. So I would never write "void * const" as a parameter type.
 
Const pointers (as opposed to pointers to const) are quite rare in my
experience. If I want an unseatable reference, I take the view that
that is what C++ references are for.
"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Nov 19 09:15PM -0800

On 11/19/2018 4:47 PM, Chris Vine wrote:
 
> Const pointers (as opposed to pointers to const) are quite rare in my
> experience. If I want an unseatable reference, I take the view that
> that is what C++ references are for.
 
Agreed. Actually, this was another little habit of mine wrt my "self"
pointer: it shall not be modified. Ack. ;^o
"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Nov 19 10:19PM -0800

On 11/19/2018 9:15 PM, Chris M. Thomasson wrote:
>>>>> On 19/11/18 10:06, Rick C. Hodgin wrote:
>>>>>> In fp_write(), what's the difference between:
>>>>>>      void* const
[...]
>> that is what C++ references are for.
 
> Agreed. Actually, this was another little habit of mine wrt my "self"
> pointer: it shall not be modified. Ack. ;^o
 
Well, here it goes again with my habit. Take a look at the self points
in my code for a fractal reverse Julia 2-ary plotter the creates a text
based PPM image called "ct_cipher_rifc.ppm":
 
https://github.com/ChrisMThomasson/fractal_cipher/blob/master/RIFC/ct_bin_ppm.c
 
________________
// Compute the fractal
void
ct_ifs(
struct ct_plane* const self,
double complex z,
double complex c,
double ratio,
unsigned long n
)
[...]
________________
 
Ummm, "struct ct_plane* const self"... ;^o
 
if you run it, there will be a new file called: "ct_cipher_rifc.ppm" on
your system. So, be aware of that.
 
Good thing this is my own personal software. Yikes!
Jorgen Grahn <grahn+nntp@snipabacken.se>: Nov 20 06:49AM

On Tue, 2018-11-20, Chris Vine wrote:
...
> Const pointers (as opposed to pointers to const) are quite rare in my
> experience. If I want an unseatable reference, I take the view that
> that is what C++ references are for.
 
I may use const pointers whenever I use pointers, just like I may use
const int whenever I use int. (I don't use pointers a lot, though.)
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
David Brown <david.brown@hesbynett.no>: Nov 20 09:27AM +0100

On 19/11/18 23:56, Chris M. Thomasson wrote:
 
> Fwiw, I put the const in the interface to attempt to get the point
> across that this parameter shall never be mutated by a function. Sort of
> a contract, documented within the API itself.
 
Except for reference parameters, parameters are /never/ mutated by a
function. This is basic stuff - parameters in C are always pass by
value. If you think putting "const" in a non-reference parameter tells
the user something useful, then either /you/ misunderstand how
parameters work in C and C++, or you think your users misunderstand it.
 
For reference parameters, it is essential to distinguish between const
and non-const parameters - precisely because with pass-by-reference a
function /can/ mutate the calling function arguments.
 
So please do not add unnecessary const qualifiers to non-reference
parameters in a function. Put const on the pointed-to types to indicate
that those are not changed by the function - but don't put const on the
parameters themselves.
 
> code are basically akin to the this parameter in C++. The value of the
> this pointer shall never be changed, just like the value of the self
> pointer shall never be changed.
 
Let me re-emphasise - this is pointless, because functions cannot change
parameters.
 
What would be /vastly/ more useful is to give sensible parameter names
in your function declarations. You have no "self" parameters in these
functions - you have anonymous parameters whose purpose is hidden.
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 20 02:40AM -0700

On Tue, 20 Nov 2018 09:27:30 +0100, David Brown
<david.brown@hesbynett.no> wrote:
 
<snip>
>parameters in a function. Put const on the pointed-to types to indicate
>that those are not changed by the function - but don't put const on the
>parameters themselves.
 
I worked with someone who added "const" to non-reference input-only
parameters. When I asked him why, he said it helped catch buggy code
that modified those parameters even if the actual function arguments
wouldn't change.
 
For example, with g++:
 
===
int square(const int x)
{
x += 1; // possibly a bug
return x * x;
}
===
mnr1.cxx: In function 'int square(int)':
mnr1.cxx:3:10: error: assignment of read-only parameter 'x'
x += 1; // possibly a bug
===
 
In Fortran, nobody would laugh at you for doing this even if the
caller's value of x wouldn't change:
 
===
integer function square(x)
implicit none
integer, value, intent(in) :: x
 
x = x + 1 ! possibly a bug
 
square = x * x
return
end
===
mnr2.f90:5:0:
 
x = x + 1 ! possibly a bug

Error: Dummy argument 'x' with INTENT(IN) in variable definition
context (assignment) at (1)
===
 
although lots of people would laugh at you just for using Fortran.
 
FWIW.
 
Louis
Ben Bacarisse <ben.usenet@bsb.me.uk>: Nov 20 11:38AM

> the user something useful, then either /you/ misunderstand how
> parameters work in C and C++, or you think your users misunderstand
> it.
 
I don't think you should make that exception for references either. In
fact, C++ forbids making a reference parameter const. You can make the
/target/ const, just as you can (and often should) for pointers, but the
parameter itself, no.
 
> For reference parameters, it is essential to distinguish between const
> and non-const parameters - precisely because with pass-by-reference a
> function /can/ mutate the calling function arguments.
 
I don't like that wording. References can never be const -- it makes no
sense. References may be to const or non const-qualified types. I know
this just words, but I think they matter when explaining things.
 
Conceptually you can have both
 
void f(int const &a);
void f(int &const a);
 
just as you can have both
 
void f(int const *a);
void f(int *const a);
 
The first of those is not a const pointer no matter how many times
people call it one -- it's a pointer to const. Similarly, the first
reference example is not a const reference. The second one would be,
were it permitted (the syntax allows it of course).
 
<snip>
--
Ben.
David Brown <david.brown@hesbynett.no>: Nov 20 12:39PM +0100

On 20/11/18 10:40, Louis Krupp wrote:
> x += 1; // possibly a bug
> return x * x;
> }
 
Putting "const" on the parameters in the function definition is a
different thing. If you find it helps avoid bugs in the code, fine.
 
> ===
 
> In Fortran, nobody would laugh at you for doing this even if the
> caller's value of x wouldn't change:
 
I have not used Fortran myself, but I believe its parameters were passed
by reference, not value. In C++ it makes sense to mark reference
parameters as const if possible.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 20 12:23PM

On 20 Nov 2018 06:49:36 GMT
> > that is what C++ references are for.
 
> I may use const pointers whenever I use pointers, just like I may use
> const int whenever I use int. (I don't use pointers a lot, though.)
 
Indeed you may. I have possibly not grasped your point, but mine was
that it would be rare to write:
 
int i;
int* const p = &i;
 
It would be more normal to write the immutable pointer in reference
form:
 
int& r = i;
 
and the same with function parameters.
 
"Rare" does not mean "never". You might want an immutable pointer if
you receive an object by pointer which you do not own and in due course
you are going to pass it to another function by pointer, and you want to
ensure (or document) that in the meantime you do not reseat the pointer
in your code.
David Brown <david.brown@hesbynett.no>: Nov 20 01:41PM +0100

On 20/11/18 12:38, Ben Bacarisse wrote:
> fact, C++ forbids making a reference parameter const. You can make the
> /target/ const, just as you can (and often should) for pointers, but the
> parameter itself, no.
 
I thought it was clear that I meant using the const to indicate that the
passed-by-reference parameter was unchanged. But on re-reading, I see I
was not clear enough. If we use "int" as our basic type, rather than
pointers, I mean that you should not write:
 
void foo(int const x);
 
or
 
void foo(const int x);
 
because the "const" adds nothing useful to the declaration, and may be
confusing. (People may think it is a distinct overload from "void
foo(int x)", for example.)
 
But with references, it /is/ important:
 
void foo(int const &x);
or
void foo(const int &x);
 
is different from
 
void foo(int &x);
 
 
> people call it one -- it's a pointer to const. Similarly, the first
> reference example is not a const reference. The second one would be,
> were it permitted (the syntax allows it of course).
 
"void f(int & const a);" is not permitted, as far as I can see - from
N3797 (C++14) section 8.3.2:
 
> are introduced through the use of a typedef-name (7.1.3, 14.1) or
> decltype-specificer (7.1.6.2), in which case the cv-qualifiers are
> ignored.
 
gcc gives an error "const qualifiers cannot be applied to int&".
 
The reference itself cannot be changed after initialisation - that is a
key feature of a reference. So as you say, it makes no sense for the
reference to be "const", as it is always unchangeable.
 
I fully agree that it is important to distinguish "pointer to const"
from "const pointer" - these both exist, with different usages, and the
distinction is critical.
 
However, the C++ standard itself uses the term "const reference" as well
as "reference to const" for things of types such as "const int&". I can
appreciate that "reference to const" is more accurate, but "const
reference" is a common phrase that does not suffer from the ambiguity or
confusion you get with "const pointer".
 
Of course, you might not like the wording in the standard either :-)
"Rick C. Hodgin" <rick.c.hodgin@gmail.com>: Nov 20 05:13AM -0800

On Monday, November 19, 2018 at 8:56:05 AM UTC-5, David Brown wrote:
> parameter itself ... is rarely helpful - it does
> not affect the calling function at all, but makes the types harder to
> read. So I would never write "void * const" as a parameter type.
 
If I wanted a pointer that can't be altered, pointing to something
that can't be altered, what would I use?
 
const void* const p;
 
--
Rick C. Hodgin
David Brown <david.brown@hesbynett.no>: Nov 20 02:24PM +0100

On 20/11/18 14:13, Rick C. Hodgin wrote:
 
> If I wanted a pointer that can't be altered, pointing to something
> that can't be altered, what would I use?
 
> const void* const p;
 
Yes, exactly.
 
 
Just to be clear - it is a good thing to make objects "const" inside
functions. You have to initialise them when you declare them, as you
can't assign to them later (IMHO it is usually a good thing to postpone
declaring local data until you are ready to initialise it). They help
stop mistakes and make it clearer to the reader (and writer!) which
objects are single valued, and which will change throughout the function.
 
It is not a bad idea to put "const" on parameters in a function
definition, if you think it helps catch errors.
 
But it is unnecessary to put "const" on parameters in a function
declaration, and can cause confusion.
James Kuyper <jameskuyper@alumni.caltech.edu>: Nov 20 09:20AM -0500

On 11/20/18 03:27, David Brown wrote:
> On 19/11/18 23:56, Chris M. Thomasson wrote:
...
>> a contract, documented within the API itself.
 
> Except for reference parameters, parameters are /never/ mutated by a
> function.
 
I think you mean "arguments", not "parameters". See the definitions of
those terms in 1.3.2 and 1.3.15 of the standard.
 
int half(int count) { return count/2; }
 
int main(void) {
int i = 10000;
int j = half(i);
return j==5000;
}
 
"count" is a parameter of the half function, and is modified by it. "i"
is the argument for a given call to that function, and is not modified
by that call.
 
A parameter is a variable local to a function, and I declare it 'const'
or not on the same basis as any other local variable: if it shouldn't be
changed from it's initial value, I declare it 'const'.
However, that's an implementation detail that is of no relevance to the
user of the function. The function declaration that is used to call a
function is allowed to lack qualifiers on parameters that have those
qualifiers in the function definition, and it's conventional to take
advantage of that fact.
Ben Bacarisse <ben.usenet@bsb.me.uk>: Nov 20 02:31PM

>> /target/ const, just as you can (and often should) for pointers, but the
>> parameter itself, no.
 
> I thought it was clear
 
No you were clear and I was not confused about what you were saying. My
point was to suggest another way of saying it which I think as some
advantages.
 
> that I meant using the const to indicate that the
> passed-by-reference parameter was unchanged.
 
That's another usage that I don't like! I was taught to distinguish
between an argument and a parameter (though sometimes these are called
actual arguments and formal arguments). In my preferred wording "a
reference to const parameter makes it clear that the argument will not
be changed".
 
<snip>
> reference" is a common phrase that does not suffer from the ambiguity or
> confusion you get with "const pointer".
 
> Of course, you might not like the wording in the standard either :-)
 
Indeed not! But it's used sparingly and the context (in the cases I
looked at) helps to avoid confusion.
 
--
Ben.
Ralf Goertz <me@myprovider.invalid>: Nov 20 03:56PM +0100

Am Tue, 20 Nov 2018 09:20:17 -0500
> return j==5000;
> }
 
> "count" is a parameter of the half function, and is modified by it.
 
 
In what way is "count" modified? It would still have the same value
after the return (but of course it is destroyed) that it had when the
function was called. Changing the function definition to
 
int half(const int count) { return count/2; }
 
compiles fine.
David Brown <david.brown@hesbynett.no>: Nov 20 04:05PM +0100

On 20/11/18 15:31, Ben Bacarisse wrote:
 
> No you were clear and I was not confused about what you were saying. My
> point was to suggest another way of saying it which I think as some
> advantages.
 
I expected that /you/ understood what I was saying, but that you thought
I could be clearer for other people. At least I could have been more
accurate in my wording.
 
> actual arguments and formal arguments). In my preferred wording "a
> reference to const parameter makes it clear that the argument will not
> be changed".
 
Fair enough. The terms "argument" and "parameter" are often mixed up,
but your usage here is more precise than mine.
 
 
>> Of course, you might not like the wording in the standard either :-)
 
> Indeed not! But it's used sparingly and the context (in the cases I
> looked at) helps to avoid confusion.
 
"Const reference" is used more often than "reference to const" in the
standard. I'm still not convinced it makes a large difference - indeed,
I am quite convinced that the person asking the question understood the
answer. But I agree that careful terminology is better than loose
terminology even if both are understood.
Robert Wessel <robertwessel2@yahoo.com>: Nov 20 09:05AM -0600

On Sun, 18 Nov 2018 19:29:38 +0100, Christian Gollwitzer
>The main difference between the C and the C++ version is that "qsort" is
>defined in a separately compiled file, while std::sort is defined in the
>header file and compiled simultaneously.
 
 
If distributed in the proper format, even a separately compiled
qsort() should be handle-able by link time code generation, if passed
a fixed pointer to the comparison function (and the comparison
function is visible as well).
 
Unfortunately, to my knowledge, that's never been done, and even if
done, LTCG often is a bit conservative about that sort of thing,
especially with routines that can be a bit larger, like qsort().
Ben Bacarisse <ben.usenet@bsb.me.uk>: Nov 20 12:50AM

> a collection of letters. For more (but perhaps still not total) clarity,
> please see my code below. It's an interview question and interviewers
> don't usually give total clarity either.
 
My interpretation (and solution) is shown below, after some space in
case some reader does not want to see other solutions just yet.
 
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
 
#include <cstring>
#include <map>
#include <iostream>
 
bool characters_match(const std::map<char, int> &counts,
const std::string &word)
{
std::map<char, int> wd_counts;
for (auto c : word)
if (counts.find(c) == counts.end() || counts.at(c) == wd_counts[c])
return false;
else wd_counts[c] += 1;
return true;
}
 
int main(int argc, char *argv[])
{
if (argc == 2) {
size_t max_len = std::strlen(argv[1]);
std::map<char, int> counts;
for (size_t i = 0; i < max_len; i++)
counts[argv[1][i]] += 1;
 
size_t cur_max_len = 0;
std::string word, best_word = "";
while (std::cin >> word)
if (word.length() > cur_max_len &&
word.length() <= max_len &&
characters_match(counts, word)) {
cur_max_len = word.length();
best_word = word;
}
std::cout << "Best: " << best_word << "\n";
}
else std::cerr << "one string argument please.\n";
}
 
--
Ben.
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 20 01:14AM -0700

On Mon, 19 Nov 2018 13:16:39 -0000 (UTC), Juha Nieminen
>as a preprocessing step). I think you can figure out how to make this
>comparison so that what's being checked is "can this word be formed
>by using letters from this group of letters?")
 
Once the letters in the pattern are sorted, you've got the makings of
a regular expression; for example, "acecad" would become
"^a{0,2}c{0,2}d?e?$" . You could sort the letters in the words as Juha
suggested and then offload the pattern matching to the Standard
Library.
 
 
>This might not be the absolutely fastest way of doing it that's
>possible, but in most cases it's efficient enough.
 
You might be able to speed it up a little by ignoring words that are
shorter than the longest word that matches the pattern.
 
If nothing else, sorting the letters and building the pattern makes it
really obvious what the code is doing. How often does an interviewer
see a candidate do that?
 
Louis
Juha Nieminen <nospam@thanks.invalid>: Nov 20 02:55PM

> "^a{0,2}c{0,2}d?e?$" . You could sort the letters in the words as Juha
> suggested and then offload the pattern matching to the Standard
> Library.
 
No need to do it that complicated.
 
Since the original word has all of its letters sorted, and the given
letters are also sorted, all you have to do is traverse both one
letter at a time, advancing in the given letters only when the
word letter doesn't match. (If a given letter becomes larger
than the word letter in this manner, you can stop because you know
that the word has a letter that's not among the input letters).
 
In other words, take the first letter of the word, and advance in
the given letters until you find it (or if a given letter becomes larger
than the word letter, you can stop because it won't be found. Likewise
if you reach the end of the given letters, of course.) Then advance in
the word letters until you encounter a different letter. Rinse and repeat
until you have gone through the entire word.
 
The above is a bit lacking as a full algorithm explanation, but I'm sure
it ought to be enough to give an idea.
Paul <pepstein5@gmail.com>: Nov 19 04:08PM -0800

On Monday, November 19, 2018 at 11:01:10 PM UTC, Jorgen Grahn wrote:
 
> I'd try to solve the problem in a way which doesn't need an unusual
> predicate. Where "unusual" means roughly anything but
> std::lexicographical_compare(f(a), f(b)), or whatever the syntax is.
 
Good advice. You say "I tried to understand it, but failed."
I'll explain what I was trying to do.
We have a std::string, for example std::string s = "ababcoddefg";
We have a std::unordered_set<std::string> stringSet;
The task is to find a longest word in stringSet that uses only letters
from string s and respects the character counts.
For example s has only 2 a's. So the word "aaa" would not be allowed but
the word "a" would be allowed (assuming that "a" is a member of stringSet).
So that raises the question of which words to try first in stringSet.
Well a word can't possibly work if it's longer than s.size() so I want
all words longer than s.size() to be at the bottom.
Regarding words that are not longer than s.size(), I want them to be
in descending order of size. That way, once you've found a word that works,
you can just return it.
I was trying to achieve the above by a sorting predicate.
I'm not saying I was right, and that my method was good.
But now I hope you understand what I was trying to do. I suppose I could
experiment by creating a large set of words and examine whether the sorting
has the above property.
 
Paul
Paul <pepstein5@gmail.com>: Nov 19 04:13PM -0800

On Monday, November 19, 2018 at 11:01:10 PM UTC, Jorgen Grahn wrote:
 
> Side note: my compiler gives me a warning about the precedence
> between || and &&. I happily admit I had to look it up; I've never
> needed to memorize that particular precedence rule.
...
 
But it doesn't need memorization or looking it up. The precedence between
|| and && can't be different from the precedence between + and *.
Surely everyone knows that true || false && false means true || (false && false) which == true.
 
Paul
Paavo Helde <myfirstname@osa.pri.ee>: Nov 20 08:30AM +0200

On 19.11.2018 16:47, Paul wrote:
> const int length = arrayOfLetters.size();
> auto cmp = [length](const std::string& word1, const std::string& word2)->bool{return word1.size() <= length && word2.size() > length || word1.size() > word2.size();};
> std::sort(sortedDictionary.begin(), sortedDictionary.end(), cmp);
 
Assume length = 3
 
Consider
x = "ab";
y = "abcd";
 
cmp(x, y) -> true
cmp(y, x) -> true
 
That's not going to fly.
Jorgen Grahn <grahn+nntp@snipabacken.se>: Nov 20 06:59AM

On Tue, 2018-11-20, Paul wrote:
> On Monday, November 19, 2018 at 11:01:10 PM UTC, Jorgen Grahn wrote:
>> On Mon, 2018-11-19, Paul wrote:
...
> ...
 
> But it doesn't need memorization or looking it up. The precedence between
> || and && can't be different from the precedence between + and *.
 
I don't immediately see a similarity between those pairs.
 
> Surely everyone knows that true || false && false means true || (false && false) which == true.
 
1. Not everyone; see above. I honestly don't know, and I've been
programming in C for almost thirty years. (I could learn the rule,
but I have never needed to.)
 
2. I don't seem to be extreme; I don't use more parentheses than many
others. (Although I prefer to introduce temporaries for complex
expressions.)
 
3. Enough people seem to feel that way, or gcc wouldn't warn.
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
Ben Bacarisse <ben.usenet@bsb.me.uk>: Nov 20 01:34PM


>> But it doesn't need memorization or looking it up. The precedence between
>> || and && can't be different from the precedence between + and *.
 
> I don't immediately see a similarity between those pairs.
 
If you tabulate result of +, *, || and && with bool operands you get
this:
 
+ | false | true || | false | true
--------+-------+------ --------+-------+------
false | false | true false | false | true
true | true | true true | true | true

* | false | true && | false | true
--------+-------+------ --------+-------+------
false | false | false false | false | false
true | false | true true | false | true
 
You'd get the same with | and &.
 
The same goes for set union and intersection. In a language with those
operators, I'd expect union to have a lower precedence than
intersection. If C++'s std::set were to add them, it would be
reasonable (at least to me!) to use operator+ and operator* for them.
 
Slightly related: if, instead of | you take ^ (exclusive or), then ^ and
& are the additive and multiplicative operators of a finite field over
{true, false}. That field is isomorphic to the one you get using
addition and multiplication modulo 2 over {0, 1}.
 
--
Ben.
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: