Tuesday, January 17, 2023

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

Muttley@dastardlyhq.com: Jan 17 09:34AM

On Mon, 16 Jan 2023 11:51:56 -0800
 
>MSVC's debugger was always pretty damn good. I have used it to debug
>nightmare multi-threaded code created by somebody else, where I had to
>pause threads at a specific line, in order to reproduce a certain bug
 
Thats fine if you know where the line is but usually with threading issues
(and to be fair non threaded code too) a bug/crash occurs due to an earlier
bug or design error which in itself goes unnoticed.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Jan 17 12:36PM -0800


> Thats fine if you know where the line is but usually with threading issues
> (and to be fair non threaded code too) a bug/crash occurs due to an earlier
> bug or design error which in itself goes unnoticed.
 
Touche. Well, if there is a bug in my OpenGL code, I have not found it
yet. Humm... So far, everything works and renders perfectly. My
debugging issue with MSVC had to do with push_back's on a vector, then
the debugger showed that it had no items. I said, shit... So, I iterated
the vector and sure enough, it was not empty and all of my vector data
(vertex, color, texture vertex, normals, ect...) was all there and
filled in with the correct data. Strange. I was thinking if the debugger
says the vector is empty, then how did all of the data get successfully
uploaded to the GPU! Argh!
 
Fwiw, here is an crude example scene of my current project:
 
https://youtu.be/n13GHyYEfLA
Ralf Goertz <me@myprovider.invalid>: Jan 17 09:19AM +0100

Am Mon, 16 Jan 2023 21:06:41 +0100
> > simply won't believe Bonita has more hubris and self delusion than
> > an angry mouse.
 
> And you don't check that Ralf's code does sth. completely different.
 
I assume you don't mean to say, that I implemented your algorithm
incorrectly (if I'm wrong about that will you please care to elaborate?)
but that my algorithm is completely different from yours. Then, yes that
was my point. I don't understand why you use such a complicated
algorithm (compared to the very few lines I needed) if it doesn't
outperform a much simpler approach. Of course one could say that I hid
the complexity behind the call to std::shuffle() but you do the same
with std::unordered_set.emplace() an co.
Muttley@dastardlyhq.com: Jan 17 09:30AM

On Tue, 17 Jan 2023 09:19:44 +0100
>but that my algorithm is completely different from yours. Then, yes that
>was my point. I don't understand why you use such a complicated
>algorithm (compared to the very few lines I needed) if it doesn't
 
You must be new here :) Complexity is Bonitas calling card.
Bonita Montero <Bonita.Montero@gmail.com>: Jan 17 02:14PM +0100

>> was my point. I don't understand why you use such a complicated
>> algorithm (compared to the very few lines I needed) if it doesn't
 
> You must be new here :) Complexity is Bonitas calling card.
 
If you want the code to be as short and as performant
as possible there's no way to program different.
Malcolm McLean <malcolm.arthur.mclean@gmail.com>: Jan 17 07:48AM -0800

On Tuesday, 17 January 2023 at 13:13:37 UTC, Bonita Montero wrote:
 
> > You must be new here :) Complexity is Bonitas calling card.
> If you want the code to be as short and as performant
> as possible there's no way to program different.
 
A shuffle followed by taking the first elements of the vector isn't a particularly
efficient way of generating a sequence of unique random numbers. But it's
not all that ineffieicnt either, and there's often an advantage in writing something
simply ans quickly from pre-existing components, rather than writing a tailor-
made, customised solution.
Bonita Montero <Bonita.Montero@gmail.com>: Jan 17 04:49PM +0100

Am 17.01.2023 um 16:48 schrieb Malcolm McLean:
 
> not all that ineffieicnt either, and there's often an advantage in writing something
> simply ans quickly from pre-existing components, rather than writing a tailor-
> made, customised solution.
 
You don't have real randomness by shuffling.
Muttley@dastardlyhq.com: Jan 17 04:25PM

On Tue, 17 Jan 2023 07:48:02 -0800 (PST)
 
>A shuffle followed by taking the first elements of the vector isn't a
>particularly
>efficient way of generating a sequence of unique random numbers. But it's
 
Just out of interest, what would be a better way? You could randomly select
elements in a container then delete that particular element so it doesn't get
used again but I'm not sure that would be very efficient beyond single digit
container sizes. Ditto starting at a random point in the container and walking
it in a random direction until you find an unused element.
Bonita Montero <Bonita.Montero@gmail.com>: Jan 17 05:32PM +0100

Am 17.01.2023 um 16:49 schrieb Bonita Montero:
>> tailor-
>> made, customised solution.
 
> You don't have real randomness by shuffling.
 
I found that you can randomly shuffle with random_shuffle or you provide
a shuffling function-object since C++17. That would be nice but if you
have a large number of values and chose only a small portion from that
this would take a lot of memory.
Ralf Goertz <me@myprovider.invalid>: Jan 17 05:31PM +0100

Am Tue, 17 Jan 2023 16:49:37 +0100
> > from pre-existing components, rather than writing a tailor- made,
> > customised solution.
 
> You don't have real randomness by shuffling.
 
What makes you say that? Have a look at the first answer to that
question:
<https://cs.stackexchange.com/questions/47338/whats-a-uniform-shuffle>.
It shows that a properly implemented shuffle makes every permutation
equally probable. What more do you need for "real randomness"? While I
haven't checked the implementation of my c++ library
(libstdc++6-devel-gcc12-12.2.1+git537-1.2.x86_64) I have no doubt that
the authors have created a suitable one.
Malcolm McLean <malcolm.arthur.mclean@gmail.com>: Jan 17 09:08AM -0800

> used again but I'm not sure that would be very efficient beyond single digit
> container sizes. Ditto starting at a random point in the container and walking
> it in a random direction until you find an unused element.
 
You have the range 0 to N-1 to choose M unique elements from.
Pick a single element x using a unifrom random number. That now gives
us two ranges, 0 to x-1 and x+1 to N-1. And M-1 numbers left to pick.
 
The number we need to pick from each range is given by the hypergeometric
distribution. We have x "good" balls and N - x - 2 "bad" balls in an urn, and
we withdraw M-1 balls. The number of "good" balls we draw tells us how many
elemets to distribute to the first half of the range. The remainder of the elements
we choose from the second half of the range.
Then it's just recursive, with the range splitting on a random elemnet each level.
 
You cna write a generator for numbers with a hypergeometric distribution naively
and quite simply by taking M random numbers. Or you can do it in a far more complicated
way by calculating the cumulative distribution function. If you do it the naive way,
the algorithm is O(M log M) (M calls to the uniform function on each level, and log M
levels of recursion). If you do it the complicated way you can improve on that.
Muttley@dastardlyhq.com: Jan 17 05:10PM

On Tue, 17 Jan 2023 17:31:57 +0100
>haven't checked the implementation of my c++ library
>(libstdc++6-devel-gcc12-12.2.1+git537-1.2.x86_64) I have no doubt that
>the authors have created a suitable one.
 
To be pedantic, there's no real randomness at all if its simply generated
from a mathematical function because you only need to know the seed to
reproduce the sequence. To be truly random you need a hardware source such as a
white noise generator. Even generators based on whats going on in the OS from
users, network input etc isn't truly random and can be biased.
Muttley@dastardlyhq.com: Jan 17 05:16PM

On Tue, 17 Jan 2023 09:08:12 -0800 (PST)
>the algorithm is O(M log M) (M calls to the uniform function on each level,
>and log M
>levels of recursion). If you do it the complicated way you can improve on that.
 
Probably looks good on a whiteboard but there's a lot of shifting stuff
about going on there so I doubt it'll be any faster (or more random) than
simply doing random walks of an array until you find an unused element. But
then its obviously based on the same algo as quicksort and selecting the
optimal sorting function is a black art when copy costs vs comparison costs
are factored in.
Bonita Montero <Bonita.Montero@gmail.com>: Jan 17 06:18PM +0100

> such as a white noise generator. Even generators based on whats going
> on in the OS from users, network input etc isn't truly random and can
> be biased.
 
You can initialize mt19937_64( (random_device())() ).
That's enough randomness for this task.
Ben Bacarisse <ben.usenet@bsb.me.uk>: Jan 17 05:32PM

>>particularly
>>efficient way of generating a sequence of unique random numbers. But it's
 
> Just out of interest, what would be a better way?
 
"Better" depends on the context. This thread has been split between
comp.lang.c and comp.lang.c++ and if you have not been reading both you
won't have seen all the many proposed algorithms. There are lots of
possible trade-offs between the number of random number call, the amount
of storage needed and sizes of the numbers involved.
 
It isn't even clear what's wanted. The OP might have wanted just a
random selection (all possible subsets equally probable) they might have
wanted a random permutation (where all possible sequences of all
possible subsets are equally probable).
 
--
Ben.
Bonita Montero <Bonita.Montero@gmail.com>: Jan 17 07:24PM +0100

Am 17.01.2023 um 17:32 schrieb Bonita Montero:
 
> a shuffling function-object since C++17. That would be nice but if you
> have a large number of values and chose only a small portion from that
> this would take a lot of memory.
 
I made a compromise between memory-consumption and performance. For
the following code if more than a tenth of the range is chosen for
the values I use a random-shuffle like operation. If less than a
tenth of the range is chosen I an unordered_set to check for col-
lisions. The decision is made depening on the PARTITIAL_THRESHOLD
variable.
 
#include <iostream>
#include <vector>
#include <charconv>
#include <random>
#include <concepts>
#include <unordered_set>
 
using namespace std;
 
int main( int argc, char **argv )
{
try
{
if( argc < 4 ) [[unlikely]]
return
cout << argv[0] << " n from to" << endl,
EXIT_FAILURE;
auto parse = []( char const *str, char const *err )
{
size_t value;
if( from_chars_result fcr = from_chars( str, str + strlen( str ),
value ); (bool)fcr.ec || *fcr.ptr ) [[unlikely]]
throw invalid_argument( err );
return value;
};
size_t n = parse( argv[1], "wrong number of values" );
if( !n ) [[unlikely]]
return EXIT_SUCCESS;
size_t
from = parse( argv[2], "wrong from-value" ),
to = parse( argv[3], "wrong to-value" );
if( from > to ) [[unlikely]]
swap( from, to );
size_t range = to - from;
if( n - 1 > range ) [[unlikely]]
return
cout << "n is too large" << endl,
EXIT_FAILURE;
vector<size_t> values;
mt19937_64 mt;
uniform_int_distribution<size_t> uidRange( 0, range );
constexpr size_t PARTITIAL_THRESHOLD = 10;
if( ++range && range / n >= PARTITIAL_THRESHOLD )
{
unordered_set<size_t> valuesSet;
valuesSet.reserve( n );
while( valuesSet.size() < n )
for( ; ; )
if( size_t value = uidRange( mt ); !valuesSet.contains( value ) )
{
valuesSet.emplace( value );
break;
}
values.resize( n );
auto itSetValue = valuesSet.cbegin();
for( size_t i = 0; i != n; )
values[i++] = *itSetValue++;
}
else
{
if( !range )
throw bad_alloc();
values.resize( range );
for( size_t i = from; size_t &v : values )
v = i++;
for( size_t i = 0; i != n; )
swap( values[i++], values[uidRange( mt )] );
values.resize( n );
}
for( size_t i = 0; i != n; ++i )
; //cout << values[i] << endl;
}
catch( exception const &exc )
{
return
cout << exc.what() << endl,
EXIT_FAILURE;
}
}
Paul N <gw7rib@aol.com>: Jan 17 04:29AM -0800

I'm attempting to write a Bridge program, and it's getting quite messy.
 
At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows:
 
enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2,
B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27,
B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42,
B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };
 
This lets me do code such as
 
if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT
if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise"); bid = B_3NT; }
else { mess = _TEXT("Not got 25 points"); } }
 
which is relatively clear.
 
However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum
 
enum { clubs, diamonds, hearts, spades, notrumps };
 
and a function
 
int b(int level, int suit) { return 15 * level + 3 * suit; }
 
which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid.
 
Part of me thinks I ought to update the first enum to be more like
 
enum { B_1NT = b(1, notrumps) }
 
but this does not work, even if I make b inline.
 
Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function?
 
All thoughts welcome!
Paavo Helde <eesnimi@osa.pri.ee>: Jan 17 03:15PM +0200

17.01.2023 14:29 Paul N kirjutas:
 
> Part of me thinks I ought to update the first enum to be more like
 
> enum { B_1NT = b(1, notrumps) }
 
> but this does not work, even if I make b inline.
 
Use constexpr or consteval, depending on if you want to use b() also
with non-compile-time arguments or not.
 
constexpr int b(int level, int suit) { return 15 * level + 3 * suit; }
"Öö Tiib" <ootiib@hot.ee>: Jan 17 05:23AM -0800

On Tuesday, 17 January 2023 at 14:29:51 UTC+2, Paul N wrote:
> B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42,
> B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
> B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };
 
Nameless type, SCREAMING CAPS names and then using just ints anyway?
 
 
> enum { clubs, diamonds, hearts, spades, notrumps };
 
> and a function
 
> int b(int level, int suit) { return 15 * level + 3 * suit; }
 
Here you clearly want to have constexpr function.

 
> Part of me thinks I ought to update the first enum to be more like
 
> enum { B_1NT = b(1, notrumps) }
 
> but this does not work, even if I make b inline.
 
You want constexpr, not inline. The constexpr implies inline.
 
> Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function?
 
You mix up constexpr and consteval here. The consteval function must run
compile time but constexpr can be ran run time as well.
Paul N <gw7rib@aol.com>: Jan 17 05:44AM -0800

On Tuesday, January 17, 2023 at 1:15:56 PM UTC, Paavo Helde wrote:
> Use constexpr or consteval, depending on if you want to use b() also
> with non-compile-time arguments or not.
 
> constexpr int b(int level, int suit) { return 15 * level + 3 * suit; }
 
Thanks Paavo, that's just the job. I didn't realise constexpr could handle both compile-time and non-compile-time values.
Paul N <gw7rib@aol.com>: Jan 17 05:52AM -0800

On Tuesday, January 17, 2023 at 1:23:47 PM UTC, Öö Tiib wrote:
> > B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
> > B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };
 
> Nameless type, SCREAMING CAPS names and then using just ints anyway?
 
Well, it's nearly a macro, isn't it? If you have any better ideas, I'd (seriously) be glad to hear them, that's the whole point of this post.
 
The values aren't random, they also represent contracts, with 16 representing 1 clubs doubled, 17 representing 1 clubs redoubled, etc.
 
 
> > and a function
 
> > int b(int level, int suit) { return 15 * level + 3 * suit; }
> Here you clearly want to have constexpr function.
 
Yes, Paavo said that too and gave me the syntax. I perhaps ought to keep up more with these recent (!) developments to the language.
 
> > Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function?
> You mix up constexpr and consteval here. The consteval function must run
> compile time but constexpr can be ran run time as well.
 
Thanks for the explanation. I'd heard of constexpr (but didn't know much about it) whereas I'd not heard of consteval.
David Brown <david.brown@hesbynett.no>: Jan 17 05:00PM +0100

On 17/01/2023 14:52, Paul N wrote:
 
> Well, it's nearly a macro, isn't it? If you have any better ideas,
> I'd (seriously) be glad to hear them, that's the whole point of this
> post.
 
Macro names don't need all caps. I only ever use all-caps names if I
have a macro that is doing something weird, and you have to pay special
attention to it. So if I have a function-like macro that behaves very
much like a function, it does not need all-caps in the name. (Of
course, in C++ this generally would be a real function - it's more
common in C, when you need something that is type generic and you don't
have templates.) Similarly, macros that are simple constants do not
need all-caps names. (And again, there are usually better choices than
macros.)
 
If the macro evaluates a parameter more than once, or plays silly
buggers with scopes, declares new variables, etc., in a way that is not
obvious in its use, then all-caps is a good warning. But overuse of a
warning negates its effectiveness.
 
 
> The values aren't random, they also represent contracts, with 16
> representing 1 clubs doubled, 17 representing 1 clubs redoubled,
> etc.
 
Then don't define them this way. Do it using a constexpr function, as
discussed in other posts, or by some completely different arrangement
(such as a const template variable, or a class that wraps the value
while storing it in a bitfield struct - there are a number of options).
Whenever there are hand-calculated magic numbers, you should be
sceptical. It /might/ be the simplest and clearest way to write the
code, but it might not be. And it's seldom the most /fun/ way to write
the code!
Paul N <gw7rib@aol.com>: Jan 17 08:17AM -0800

On Tuesday, January 17, 2023 at 4:00:38 PM UTC, David Brown wrote:
> sceptical. It /might/ be the simplest and clearest way to write the
> code, but it might not be. And it's seldom the most /fun/ way to write
> the code!
 
Thanks for the comments, David. My first attempt at the code used all the magic numbers and so having an enum was at least an improvement on that. Now that I have the function, I have more options, but it's still not entirely clear to me whether changing code from the already written bid = B_3NT; to the slightly longer bid = b(3, notrumps) is really an improvement. Hence the canvassing of opinions.
Bo Persson <bo@bo-persson.se>: Jan 17 06:43PM +0100

On 2023-01-17 at 14:52, Paul N wrote:
 
>>> int b(int level, int suit) { return 15 * level + 3 * suit; }
 
>> Here you clearly want to have constexpr function.
 
> Yes, Paavo said that too and gave me the syntax. I perhaps ought to keep up more with these recent (!) developments to the language.
 
Yes, as recent as 2011. :-)
Ralf Goertz <me@myprovider.invalid>: Jan 17 09:21AM +0100

removed with Claws Mail
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: