Wednesday, December 4, 2019

Digest for comp.lang.c++@googlegroups.com - 17 updates in 5 topics

"Öö Tiib" <ootiib@hot.ee>: Dec 04 10:26AM -0800

On Wednesday, 4 December 2019 18:37:50 UTC+2, Taylor wrote:
> return node;
> }
 
> (This kind of stuff is all over the place, because it eliminates a LOT of redundant code)
 
Huh? The design feels odd indeed.
I have usually stuff in variants that have rare things if anything
at all in common.
 
> }
> return node;
> }
 
External setter? I do not have setter members no idea why I
would want external setters.
 
 
> I've found that if I simply make Node a struct with every possible data member from all the variant types, it only increases the size of Node by a factor of two, which is totally fine. (I could use std::optional for each member, but that would increase the size, and potentially be more dynamic than I need)
 
> Does that sound like the approach you would take?
 
No. Only by factor of two? Then those are rather similar classes.
 
From 68 mostly similar to each other "node" classes I would make plain
old polymorphic hierarchy.
If there is need to keep objects of these classes in a single
container then I would use Boost.PolyCollection for efficiency.
https://www.boost.org/doc/libs/1_71_0/doc/html/poly_collection.html
 
That design beats both your variant and your monster struct blindfolded
with right hand tied behind back I can bet.
 
> thanks!
 
Oh ... no problem.
Taylor <wtholliday@gmail.com>: Dec 04 11:07AM -0800

Thanks for your help!
 
On Wednesday, December 4, 2019 at 10:26:52 AM UTC-8, Öö Tiib wrote:
 
> Huh? The design feels odd indeed.
> I have usually stuff in variants that have rare things if anything
> at all in common.
 
Consider what the function does: it's checking (statically) if the struct has a particular member and setting it.
 
 
> No. Only by factor of two? Then those are rather similar classes.
 
> From 68 mostly similar to each other "node" classes I would make plain
> old polymorphic hierarchy.
 
I used to do a traditional polymorphic hierarchy: it's harder to make value types, and requires much more boilerplate.
 
> https://www.boost.org/doc/libs/1_71_0/doc/html/poly_collection.html
 
> That design beats both your variant and your monster struct blindfolded
> with right hand tied behind back I can bet.
 
I'm using https://github.com/arximboldi/immer and can't change my containers.
"Öö Tiib" <ootiib@hot.ee>: Dec 04 12:31PM -0800

On Wednesday, 4 December 2019 21:07:37 UTC+2, Taylor wrote:
> > I have usually stuff in variants that have rare things if anything
> > at all in common.
 
> Consider what the function does: it's checking (statically) if the struct has a particular member and setting it.
 
Do not let complex syntax to confuse you ... magic does not exist.
It can't. It generates switch-case that checks *dynamically* behind scenes
what of those 68 is in that particular variant instance "node" and then
sets the member.
 
 
> > From 68 mostly similar to each other "node" classes I would make plain
> > old polymorphic hierarchy.
 
> I used to do a traditional polymorphic hierarchy: it's harder to make value types, and requires much more boilerplate.
 
Value types in sense that little, immutable objects, passed by value?
These do not make sense in context of your post at all.
 
On the contrary, it is boilerplate of piles of those inefficient
switches from 68 cases all around your code that traditional
polymorphic hierarchy would remove with just one cheap level of
indirection added. I have measured it enough to predict that
program would be at least 4 times quicker, data about 4 times
smaller and executable also about twice smaller.
 
 
> > That design beats both your variant and your monster struct blindfolded
> > with right hand tied behind back I can bet.
 
> I'm using https://github.com/arximboldi/immer ...
 
I do not know the lib but it is seems from afar that public common
setters sound like total bullshit with those immers that are
immutable after construction. Come again and tell what you really
need.
 
> ... and can't change my containers.
 
That is defect of mind. Software is *soft*. It is like drawing on
sand not carving on obsidian.
 
Most projects usually combine data initially from rather few container
types like:
std::array, std::vector, std::unordered_set, std::unordered_map.
Then, later, when the needs become more clear those are maintained
to be more special, precisely suitable containers.
Taylor <wtholliday@gmail.com>: Dec 04 01:09PM -0800

On Wednesday, December 4, 2019 at 12:31:31 PM UTC-8, Öö Tiib wrote:
> It can't. It generates switch-case that checks *dynamically* behind scenes
> what of those 68 is in that particular variant instance "node" and then
> sets the member.
 
I'm not confused. The determination of whether a struct has a member is done statically via SFINAE. Specifically `decltype(n.exposedPosition)* = nullptr`. Of course *which* struct is of course dynamic.
 
 
> > I used to do a traditional polymorphic hierarchy: it's harder to make value types, and requires much more boilerplate.
 
> Value types in sense that little, immutable objects, passed by value?
> These do not make sense in context of your post at all.
 
Not necessarily immutable, but yes passed by value. I'm using value-oriented design.
 
> indirection added. I have measured it enough to predict that
> program would be at least 4 times quicker, data about 4 times
> smaller and executable also about twice smaller.
 
This is not runtime performance critical code. (That's all on another thread)
 
 
> > ... and can't change my containers.
 
> That is defect of mind. Software is *soft*. It is like drawing on
> sand not carving on obsidian.
 
Of course, but it takes time to make bigger changes.
 
> std::array, std::vector, std::unordered_set, std::unordered_map.
> Then, later, when the needs become more clear those are maintained
> to be more special, precisely suitable containers.
 
There are big advantages to be had with using those Immer containers which I would not want to abandon.
 
cheers
David Brown <david.brown@hesbynett.no>: Dec 04 10:34PM +0100


> 1. Compile times are extremely slow (~30s per file), due to variant
> visitation. I'm an indie developer and I don't want to have to buy a
> new computer (might only be a factor of two faster anyway).
 
There are many possibilities for speeding up compilation. But we'd need
a lot more information to be able to tell. I can only give some vague
suggestions (in no particular order), without knowing which might apply.
 
1. If your computer is very slow, get a better one - or upgrade the one
you have (more memory is often helpful).
 
2. Consider changing compilers if the compiler is slow.
 
3. If you are working with lots of files in the project, make sure you
are using a good build system with parallel builds.
 
4. If you have everything in a few huge files, split them up (and use a
parallel build system).
 
5. If you are using Windows and have parallel builds and lots of files,
especially with gcc or clang, try Linux. If you are stuck with Windows,
try with an SSD instead of a hard disk.
 
6. Look at pre-compiled header support.
 
7. Check the compiler flags. You'll want some optimisation, but be
careful about using some of the more powerful ones (especially ones that
are not part of any -O option), as they can involve a great deal of
compiler effort. But if you have no optimisation, the compiler will
have to generate huge amounts of code, which can be slower than optimising.
 
8. Consider something other than variant. A variant needs quite a bit
of compile-time effort to get its static type safety and convenience,
and a 68 type variant is going to involve a very large amount of
compile-time effort.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Dec 04 10:39PM +0100

On 04.12.2019 22:09, Taylor wrote:
>> what of those 68 is in that particular variant instance "node" and then
>> sets the member.
 
> I'm not confused. The determination of whether a struct has a member is done statically via SFINAE. Specifically `decltype(n.exposedPosition)* = nullptr`. Of course *which* struct is of course dynamic.
 
I, on the other hand, am confused.
 
I remember reading the original blog posting (on a blog by some
"experts", I think Dave Abrahams was there) about functionality like
`overloaded`, to make a set of lambdas available via a single function
that dispatches to them as if with overload resolution.
 
But now I fail to see, I don't remember, quite how it worked.
 
As I see it the OP shouldn't assume that readers would be familiar with
`overloaded`.
 
When I can't imagine it and only vaguely remember reading about it, then
I think most regulars here know just as little about it as I. And
delving into links to third party web pages. Well that's just not done. :)
 
 
[snip]
 
- Alf
"Öö Tiib" <ootiib@hot.ee>: Dec 04 02:46PM -0800

On Wednesday, 4 December 2019 23:09:54 UTC+2, Taylor wrote:
> > what of those 68 is in that particular variant instance "node" and then
> > sets the member.
 
> I'm not confused. The determination of whether a struct has a member is done statically via SFINAE. Specifically `decltype(n.exposedPosition)* = nullptr`. Of course *which* struct is of course dynamic.
 
That is exactly what I meant.
Compiler generates dynamic switch over 68 cases, each case block
made with SFINAE for type handled in that block. Basically just
odd syntax sugar to hide long switch-case. You yourself said you
have very numerous of those. Therefore certain combo of classical
dynamic polymorphism and classical visitor pattern will win the
huge variant of everything (my modules handle rarely over 50 classes)
in all areas easily and without much doubt.
 
 
> > Value types in sense that little, immutable objects, passed by value?
> > These do not make sense in context of your post at all.
 
> Not necessarily immutable, but yes passed by value. I'm using value-oriented design.
 
Yeah I noticed that you both pass and return that variant as value
in your examples. Are you sure that "value-oriented design" means
that "always make copies when possible"?
 
> > program would be at least 4 times quicker, data about 4 times
> > smaller and executable also about twice smaller.
 
> This is not runtime performance critical code. (That's all on another thread)
 
It was you who said that people "might be baffled by the c++ fancyness"
of your code. There are no performance, no robustness, no clarity,
no readability ... what *is* the point?
 
 
> > That is defect of mind. Software is *soft*. It is like drawing on
> > sand not carving on obsidian.
 
> Of course, but it takes time to make bigger changes.
 
True. That is why I keep my stuff very modular. Bit more work
with making and keeping the architecture clean but lot easier to
replace the parts with better or even to have (optional) alternatives
side-by-side.
 
> > Then, later, when the needs become more clear those are maintained
> > to be more special, precisely suitable containers.
 
> There are big advantages to be had with using those Immer containers which I would not want to abandon.
 
Huh? When you are collector of worthless recommendations then ask for
advice but keep the situation that you are in and constraints that you
have ... in secrecy. Instead make confusing claims with strange
examples. ;) That immer seemed to be all for performance in concurrent
processing. But just plain old "favor immutability" does not need
special libraries for it so it did not make me to feel interested.
alexo <alelvb@inwind.it>: Dec 04 05:54PM +0100

Hello guys,
I've written this code to get acquainted to assignment operators,
move constructors and move assignment operators, but I got a
strange behavior in my code:
I report it in its fullness because it's 123 lines:
 
// code starts here
#include <iostream>
 
using std::cout;
using std::ostream;
 
class Vector
{
friend ostream& operator<<(ostream& os, Vector& v)
{
for(size_t i=0; i<v.size(); i++)
os << v[i] << ' ';
 
return os << '\n';
}
 
public:
Vector(int n=5) : sz(n), data( new int[n])
{
for(size_t i=0; i < sz; i++)
data[i] = 0;
}
 
Vector(const Vector& r) : // the copy constructor
sz(r.size()),
data( new int[sz] )
{
for(size_t i=0; i < sz; i++)
data[i] = r.data[i];
}
 
 
Vector(const Vector&& r) // the move constructor
sz{r.size()},
data{r.data}
{ }
 
Vector& operator= (const Vector& r) // the copy assignment
{
delete [] data;
 
sz = r.size();
data = new int[sz];
 
for(size_t i=0; i < sz; i++)
data[i] = r.data[i];
 
return *this;
}
 
Vector& operator= (const Vector&& r) // the move assignment
{
delete [] data;
 
data = new int[r.size()];
data = r.data;
 
return *this;
}
 
~Vector() // the destructor
{
cout << "deleting memory of array ";
cout << *this;
 
delete [] data;
data = nullptr;
}
 
unsigned int size() const { return sz; }
 
int& operator[] (int i) { return data[i]; }
 
Vector operator+(Vector& rhs)
{
size_t maxdim = sz, mindim = sz;
 
if(sz < rhs.size())
{
maxdim = rhs.size();
}
else
{
mindim = rhs.size();
}
 
Vector temp (maxdim);
 
for(size_t i=0; i < mindim; i++)
temp[i] = data[i] + rhs[i];
 
return temp;
}
 
private:
size_t sz;
int *data;
};
 
int main()
{
Vector v1(3), v2;
 
cout << "v1.size() = " << v1.size() << '\n'
<< "v2.size() = " << v2.size() << "\n\n";
 
v1[1] = 1; v2[2] = 3; // using the index of '[]' operator
 
cout << "v1 = " << v1;
cout << "v2 = " << v2;
 
 
Vector s;
s = v1 + v2; // these two lines give runtime error
 
// Vector s = v1 + v2; // this line is ok instead
 
cout << "v1 + v2 = " << s << '\n';
 
cout << "destroying objects..." << '\n';
 
return 0;
}
// code ends here
 
 
Now I give a description of the error.
g++ executing this code on my gives:
 
v1.size() = 3
v2.size() = 5
 
v1 = 0 1 0
v2 = 0 0 3 0 0
deleting memory of array 0 1 3 0 0
v1 + v2 = 0 0 -1704935408 22058 0
 
destroying objects...
deleting memory of array 0 0 -1704935408 22058 0
free(): double free detected in tcache 2
aborted
 
As the title of the message says, I'm stuck.
I did my best to write it, but evidently some subtle error lays
between those lines.
 
If you comment the following:
 
Vector s;
s = v1 + v2;
 
and uncomment
 
// Vector s = v1 + v2;
 
the program behaves correctly.
What could it be the problem?
 
Thank you.
Volker Wippel <Volker.Wippel@v2c2RemoveNoSpam.at>: Dec 04 06:40PM +0100

On 2019-12-04 17:54, alexo wrote:
 
> the program behaves correctly.
> What could it be the problem?
 
> Thank you.
 
Hi
 
Vector(Vector&& r) // no const
sz{r.size()},
data{r.data}
{
r.data = nullptr; // you move from it, so r is empty
r.sz = 0;
}
 
Same in operator=(Vector&& r)
 
data{std::move(r.data)} would not help because it is a simple pointer.
Daniel <danielaparker@gmail.com>: Dec 04 10:01AM -0800

On Wednesday, December 4, 2019 at 11:54:55 AM UTC-5, alexo wrote:
> }
> // code ends here
 
> What could it be the problem?
 
One, your assignment operator defined, needs to check if this and &v are the same,
 
Vector& operator=(const Vector& v)
{
if (this != &v)
{
// ...
}
return *this;
}
 
Two, your move constructor and move assignment operator are wrong, the parameter should be Vector&& r, not const Vector&& r, and the implementation should take r.data and replace r.data with nullptr, and take r.sz and replace r.sz with 0.
 
Three, size() should return size_t.
 
Four, you need a const version of operator[](int i)
 
Daniel
Daniel <danielaparker@gmail.com>: Dec 04 10:06AM -0800

On Wednesday, December 4, 2019 at 1:01:15 PM UTC-5, Daniel wrote:
> parameter should be Vector&& r, not const Vector&& r, and the implementation
> should take r.data and replace r.data with nullptr, and take r.sz and
> replace r.sz with 0, for the move constructor, and swap data for r.data and > sz for r.sz, in the move assignment operator.
 
Daniel
Manfred <noname@add.invalid>: Dec 04 08:16PM +0100

On 12/4/2019 5:54 PM, alexo wrote:
>             data[i] = r.data[i];
>     }
 
>     // Vector(const Vector&& r)               // the move constructor
Vector(Vector&& r) : // the move constructor
>             sz{r.size()},
>             data{r.data}
>     {
r.sz = 0;
r.data = nullptr;
 
}
 
>     // Vector& operator= (const Vector& r)    // the copy assignment
Vector& operator= (Vector& r) // the copy assignment
 
>     Vector& operator= (const Vector&& r)    // the move assignment
>     {
>         delete [] data;
 
// loose the following
>         // data = new int[r.size()];
>         data = r.data;
s.sz = r.sz;
r.data = nullptr;
r.sz = 0;
 
>     {
>         cout << "deleting memory of array ";
>         cout << *this;
 
if (data)
{
>         delete [] data;
>         data = nullptr;
}
 
>     return 0;
> }
> // code ends here
 
The following is a good help:
 
c++ -std=c++17 -Wall yetAnotherVector.cc && valgrind ./a.out
 
 
alexo <alelvb@inwind.it>: Dec 04 10:52PM +0100

Il 04/12/19 17:54, alexo ha scritto:
> I've written this code to get acquainted to assignment operators,
> move constructors and move assignment operators, but I got a
> strange behavior in my code:
 
I post one answer to all of you that replied to my request:
 
@Manfred:
 
I didn't know what Valgrind was. Your last line showed me how to
invoke it. If I have well understood it is a memory analyzer tool.
 
@Volker Wippel:
 
You are right, I didn't think about that:
in the copy assignment operator the data copy should be only
performed if the righ hand side of the = operator is not the same
as the left hand side.
 
@Daniel
 
your point 4)
 
I added
 
int& operator[] (int i) const { return data[i]; }
 
to my code, but I had to modify operator<< as well:
 
friend ostream& operator<<(ostream& os, const Vector& v)
 
I thank you all for your prompt reply.
Vir Campestris <vir.campestris@invalid.invalid>: Dec 04 09:46PM

On 04/12/2019 09:20, T wrote:
> 36 bit?  My brain hurts
 
I've used systems where the native word size is 16, 24, 32, 36 and 64
bit. Other values are available :(
 
The one that really strained C was the one where incrementing a pointer
by 1 went to the next bit. Not byte or word. (TI GPU)
 
The one with 36 bits (DECSystem10) an address was the address of a word,
and you couldn't point to a character inside the word. I didn't use C on it.
 
Andy
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Dec 04 06:36PM

>>> one else gives a rats arse.
 
>> You just contradicted yourself, dear.
 
> No, its my accurate opinion that no one else gives a damn.
 
That is not just speaking for yourself, dear.
 
/Flibble
 
--
"Snakes didn't evolve, instead talking snakes with legs changed into snakes." - Rick C. Hodgin
 
"You won't burn in hell. But be nice anyway." – Ricky Gervais
 
"I see Atheists are fighting and killing each other again, over who doesn't believe in any God the most. Oh, no..wait.. that never happens." – Ricky Gervais
 
"Suppose it's all true, and you walk up to the pearly gates, and are confronted by God," Byrne asked on his show The Meaning of Life. "What will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a world that is so full of injustice and pain. That's what I would say."
boltar@nowhere.co.uk: Dec 04 04:53PM

On Wed, 4 Dec 2019 14:32:37 +0100
>Stuart
 
>PS: I was lying a little bit. When my managed C++ libraries wouldn't load
>because boost::thread uses TLS somewhere inside, I spent an awful great
 
Boost? Ugh. The sooner that hideous bloatware buggers off down the same rubbish
chute as Roguewaves APIs did back in the 00s the better. There's been little
good reason to use it since c++14 came out.
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Dec 04 06:34PM


> Boost? Ugh. The sooner that hideous bloatware buggers off down the same rubbish
> chute as Roguewaves APIs did back in the 00s the better. There's been little
> good reason to use it since c++14 came out.
 
That's because a good deal of boost is now part of C++; if boost was rubbish that wouldn't have happened, idiot.
 
/Flibble
 
--
"Snakes didn't evolve, instead talking snakes with legs changed into snakes." - Rick C. Hodgin
 
"You won't burn in hell. But be nice anyway." – Ricky Gervais
 
"I see Atheists are fighting and killing each other again, over who doesn't believe in any God the most. Oh, no..wait.. that never happens." – Ricky Gervais
 
"Suppose it's all true, and you walk up to the pearly gates, and are confronted by God," Byrne asked on his show The Meaning of Life. "What will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a world that is so full of injustice and pain. That's what I would say."
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: