Friday, February 3, 2017

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

woodbrian77@gmail.com: Feb 02 08:34PM -0800

On Thursday, February 2, 2017 at 1:38:34 PM UTC-6, Alf P. Steinbach wrote:
> pre-built variants like debug+DLL+multithreaded runtime, etc.
 
> The main disadvantage is also related to building: that essentially
> everything needs to be compiled when one re-builds something.
 
Another disadvantage is that the size of executables grows.
That may be a bigger disadvantage than increased build times.
 
 
Brian
Ebenezer Enterprises
http://webEbenezer.net
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 03 05:37AM +0100


>> The main disadvantage is also related to building: that essentially
>> everything needs to be compiled when one re-builds something.
 
> Another disadvantage is that the size of executables grows.
 
Huh. How, do you think?
 
 
> That may be a bigger disadvantage than increased build times.
 
 
Cheers!,
 
- Alf
woodbrian77@gmail.com: Feb 02 09:24PM -0800

On Thursday, February 2, 2017 at 10:38:17 PM UTC-6, Alf P. Steinbach wrote:
> >> everything needs to be compiled when one re-builds something.
 
> > Another disadvantage is that the size of executables grows.
 
> Huh. How, do you think?
 
I wasn't sure if you meant why it grows or why that's not good.
I'll assume you meant the latter. I think there's more
redundancy in executables built from header only libs. Say
the header only version of a program is 100k and the alternative
is 85k. The smaller version is easier for an operating system
to work with.
 
 
Brian
Ebenezer Enterprises
http://webEbenezer.net
Gareth Owen <gwowen@gmail.com>: Feb 03 06:37AM

> redundancy in executables built from header only libs. Say
> the header only version of a program is 100k and the alternative
> is 85k.
 
Oh, I know this. "What is 'Begging the question', Alex?"
Wouter van Ooijen <wouter@voti.nl>: Feb 03 08:01AM +0100

> the header only version of a program is 100k and the alternative
> is 85k. The smaller version is easier for an operating system
> to work with.
 
But why would the header-only version be larger?
 
Wouter "Objects? No Thanks!" van Ooijen
Robert Wessel <robertwessel2@yahoo.com>: Feb 03 01:16AM -0600

On Fri, 3 Feb 2017 08:01:23 +0100, Wouter van Ooijen <wouter@voti.nl>
wrote:
 
>> to work with.
 
>But why would the header-only version be larger?
 
>Wouter "Objects? No Thanks!" van Ooijen
 
 
Because it may well have duplicated code in a number of places that
might otherwise have been a single copy in a linked in library.
Similar to how using std::sort<> instead of qsort() can lead to bigger
code, since you'll get a copy of the sort code for each
specialization. OTOH, the specialized code may well run faster.
 
And this is at least somewhat a QoI issue - there's little preventing
a compiler and linker from creating specializations of qsort if the
code is available. Or from the implementation from implementing
std::sort as little more than a single line call to qsort (yes, it
would not actually be quite that simple, but you could leave the
majority of the code in a library so that it has overhead more like
qsort calling an external comparison function).
David Brown <david.brown@hesbynett.no>: Feb 03 08:54AM +0100

On 03/02/17 08:16, Robert Wessel wrote:
> would not actually be quite that simple, but you could leave the
> majority of the code in a library so that it has overhead more like
> qsort calling an external comparison function).
 
There is also the case that with templates, you quite often only need
one instance in the program. std::sort<double> is going to generate
smaller code than a general library function that can handle multiple
different types. A template class might have dozens of methods of which
you only use a few - with header-only versions, only those few get
generated while with a library version, /all/ of them are needed.
 
I think Brian's fear of template code size stems from the old days in
which toolchains effectively generated a new copy of each used template
function/method for every file that used it, leading to bloat.
Manfred <noname@invalid.add>: Feb 03 02:28PM +0100

On 2/2/2017 8:37 PM, Alf P. Steinbach wrote:
> based on a module implementation in clang, and now being fleshed out via
> the Visual C++ compiler. IIRC it's Gabriel Dos Reyes (speling?) and one
> other guy doing this.
Gabriel Dos Reis
"Öö Tiib" <ootiib@hot.ee>: Feb 03 06:27AM -0800

On Thursday, 2 February 2017 21:11:29 UTC+2, Christiano wrote:
> to the C world where if someone write body code definitions in headers is
> considered someone who is learning, a beginner making mistakes. The
> separation between definition and statement is very clear.
 
You likely oversimplify C++.
 
When I write my own classes then those are usually straight
to the point, exactly what is needed in concrete application.
Also only bare minimum what is needed for other classes
of application is exposed in header files.
 
When someone writes libraries like in Boost then they are
trying to be maximally generic, flexible and feature-rich.
That means templates. However hiding information to have
faster builds and reduced dependencies is impossible to do
with templates currently.
 
> natural way of thinking of C. In fact whenever I used libraries made in C,
> I never saw anything that was "header-only".
 
> So, what's the point? Why in C ++ is this way?
 
The problem is with that #include text copy-paste interface.
That is good for C but is screwed in C++ because it does not
work with templates. That #include thingy simply can't pull
the weight.
Wouter van Ooijen <wouter@voti.nl>: Feb 03 05:29PM +0100

Op 03-Feb-17 om 08:16 schreef Robert Wessel:
 
>> Wouter "Objects? No Thanks!" van Ooijen
 
> Because it may well have duplicated code in a number of places that
> might otherwise have been a single copy in a linked in library.
 
The same design, minimally modified for a headers-only approach, will
generate the same code.
 
> Similar to how using std::sort<> instead of qsort() can lead to bigger
> code, since you'll get a copy of the sort code for each
> specialization. OTOH, the specialized code may well run faster.
 
But that not due to the header-only nature of sort<> but to the fact
that it uses templates, which is not possible with a split
header/implementation approach.
 
The line I was specifically responding to was "redundancy in executables
built from header only libs" which suggest that the writer thinks that
each time a header is included a separate block of code will be
generated, all to be included in the executable. It is possible to
achieve this, but it is far from typical.
 
Wouter "Objects? No Thanks!" van Ooijen
woodbrian77@gmail.com: Feb 03 09:30AM -0800

On Friday, February 3, 2017 at 1:54:15 AM UTC-6, David Brown wrote:
 
> I think Brian's fear of template code size stems from the old days in
> which toolchains effectively generated a new copy of each used template
> function/method for every file that used it, leading to bloat.
 
I didn't use the word template.
 
In this file
https://github.com/Ebenezer-group/onwards/blob/master/ErrorWords.cc
 
I have this function:
 
cmw::failure& cmw::failure::operator<< (char const* s)
{
whatStr.append(s);
return *this;
}
 
If I move the implementation into the header, the size of
an executable increases by 511 bytes (over 1%). That's
using GCC 7 with -O2 on FreeBSD 12. Something similar happens
with Clang 3.9.1.
 
 
Brian
Ebenezer Enterprises - "Fear of man will prove to be a snare,
but whoever trusts in the L-RD is kept safe." Proverbs 29:25
 
http://webEbenezer.net
Wouter van Ooijen <wouter@voti.nl>: Feb 03 07:39PM +0100

> }
 
> If I move the implementation into the header, the size of
> an executable increases by 511 bytes (over 1%).
 
And if you move it to the header and mark it as never_inline (with
whatever syntax your compiler uses for that)?
 
Wouter "Objects? No Thanks!" van Ooijen
Jorgen Grahn <grahn+nntp@snipabacken.se>: Feb 03 07:19PM

On Thu, 2017-02-02, Christiano wrote:
> I come from the C language. I have the impression that in C ++ the ideal
> is "C ++ header only" libraries.
 
My view on that is, that what the authors of some library used by
thousands of others do, has very little to do with what *I* do to
my own non-library code.
 
You don't have to write your own code that way. Possibly, you
shouldn't even try.
 
> someone write body code definitions in headers is considered someone
> who is learning, a beginner making mistakes. The separation between
> definition and statement is very clear.
 
Maybe not so much in the past two decades, after the 'inline' keyword
got introduced with C99. Look at the Linux kernel, for example: there
are plenty of inline functions in header files, and they are carefully
chosen to balance readability and performance.
 
> the natural way of thinking of C. In fact whenever I used libraries
> made in C, I never saw anything that was "header-only".
 
> So, what's the point? Why in C ++ is this way?
 
Why not?
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
woodbrian77@gmail.com: Feb 03 12:00PM -0800

On Friday, February 3, 2017 at 12:40:10 PM UTC-6, Wouter van Ooijen wrote:
> > an executable increases by 511 bytes (over 1%).
 
> And if you move it to the header and mark it as never_inline (with
> whatever syntax your compiler uses for that)?
 
That helps a little, but the text segment of the executable is
still 336 bytes larger than when it's in the .cc.
 
36,881 -- in the .cc
37,217 -- in the .hh using __noinline
37,392 -- in the .hh
 
 
Brian
Ian Collins <ian-news@hotmail.com>: Feb 04 12:23PM +1300


> 36,881 -- in the .cc
> 37,217 -- in the .hh using __noinline
> 37,392 -- in the .hh
 
Does anyone, other than you, care?
 
--
Ian
Andrey Tarasevich <andreytarasevich@hotmail.com>: Feb 03 03:29PM -0800

On 2/2/2017 11:11 AM, Christiano wrote:
 
> So, what's the point? Why in C ++ is this way?
 
On the one hand, in the traditional approach "header-only" libraries in
C++ are typically purely (or predominantly) _template_ libraries. They
consist of "immaterial" template definitions. In such cases
"header-only" is not a goal in itself, it is a natural consequence (or
even a necessity) rooted in the functionality of templates. The moment a
C++ library acquires a regular "material" definition, like a global
variable or a non-inline function, it usually ceases to be "header-only".
 
The same thing would happen is you decided to write a C library
consisting of macros and nothing else. You'd have no choice but to put
everything into a header file. You'd ended up with a typical
"header-only" library. No way around it.
 
On the other hand, if some C++ library is 99.9% template/inline and only
a very small portion of it are "regular" functions (i.e. functions that
would typically use non-header definitions), then one can say that
erecting the traditional .h/.cpp/.a infrastructure on both the library
side and client side just for some odd function might be an overkill. In
cases like that it might make more sense to keep things "header-only" by
declaring the function 'inline' (even if it is large).
 
However, I'm also not surprised we see a tendency towards _non-template_
"header-only" libraries these days, i.e. libraries where everything is
just indiscriminately declared 'inline'. It is just easier to write,
maintain and use, even if it requires the compiler to waste quite a bit
of effort translating it [almost] from scratch every time it is
#include-d and then eliminating the resultant code duplication. Within
certain reasonable limits this is a viable approach.
 
--
Best regards,
Andrey Tarasevich
ram@zedat.fu-berlin.de (Stefan Ram): Feb 03 07:57PM

>execution time of certain parts of the program, I will not
>have time to delve deeper into it; but still, I tried to
>optimize my code.
 
Ok, we can consider the following cases:
 
First Case:
 
The string is artificially manufactured to have no
repetitions of characters.
 
In this case, my algorithm immediately sees that the
first character is unique and exits very early and thus
it is fast in this case.
 
Second Case:
 
The string is artificially manufactured to have only
repetitions of characters. I.e., it only consists of
the repetition of one character. This is kind of like
the opposite of the preceding case.
 
In this case, my algorithm has to search until the end
of the string. It seems to be the worst case for my
algorithm. But no, it is not: The inner loop now is very
short, since it immediately finds that the current character
is being repeated.
 
So, in these two cases my algorithm is more linear than
quadratic because either the outer or the inner loop
exits nearly immediately.
 
Third Case:
 
The string is filled with random characters that remotely
resemble English text, i.e., with the common characters
and digits. This is the worst case for my algorithm I have
seen so far. But even in this case my algorithm is quite
fast, because the inner loop usually is only repeated on
the average for less than about 7 percent of the whole
lenght of the string. So the processor cache can be used
even for longer strings, because the inner loop often
will not need to pass through the whole string.
 
Here are literal test results:
 
*** BEGIN OF PROGRAM OUTPUT
 
fillmode = 0filling with set of commonly used characters
average length of a string = 50000
runtime of first_unique_char_of = 65007000
average number of outer loops = 50000
average number of inner loops = 59.5878
runtime of first_unique_char_in = 11949813000
 
fillmode = 1filling with wide range of random numbers
average length of a string = 50000
runtime of first_unique_char_of = 2000000
average number of outer loops = 0.8
average number of inner loops = 40382.2
runtime of first_unique_char_in = 129004000
 
fillmode = 2filling with incrementing characters 1, 2, 3 ...
average length of a string = 50000
runtime of first_unique_char_of = 2000000
average number of outer loops = 0
average number of inner loops = 50000
runtime of first_unique_char_in = 105011000
 
fillmode = 3filling with the character 0
average length of a string = 50000
runtime of first_unique_char_of = 3001000
average number of outer loops = 50000
average number of inner loops = 2e-005
runtime of first_unique_char_in = 705320281000
 
*** END OF PROGRAM OUTPUT
 
In the first case »fillmode = 0filling with set of commonly
used characters«, the output »average number of outer loops
= 50000« indicates that there is no unique character found.
My algorithm does 5000 outer loops each with about 60 inner
loops. 60 inner loops in a cache line can be faster than
a single memory access, so it takes one little runtime
 
One can see that in the case when the whole string is
filled with the character 0, first_unique_char_in takes
a large amount of time. Probably because it can not exit
 
for( tchar const ch : s )if( chars.count( ch )== 1 )return ch;
 
early, but has to loop through the whole string.
first_unique_char_of also has to loop through the whole
string in this case, but on each iteration it usually does
only one iteration in the same cache line, which cane
be done very fast.
 
Feel free to do your own tests guys using the following
source code. It also contains the newest version of
first_unique_char_of with one bug removed (the sentinel
was not always removed). The code below was used to get
the output above, which then was slightly edited manually.
 
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iterator>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>
#include <unordered_set>
 
using namespace ::std::literals;
 
/* fillmode
0: fill strings randomly with common characters such as letters and digits
1: fill strings randomly with random characters
2: fill strings with characters with increasing consecutive codes
3: fill string with the same character at every position (try to
avoid repetitions as the worst case for some algorithms)*/
int fillmode = -1;
 
/* verify
compare results of different implementations*/
constexpr bool verify = false;
 
/* measure_in */
constexpr bool measure_in = true;
 
/* zz
the size of the strings to be generated */
constexpr unsigned long long zz = 50'000L;
 
/* stringcount
how many strings to put into the vector of strings.
The algorithms will be called for each string in this
vector. */
auto stringcount = 10;
 
/* loops
how many times to iterate the whole test.
(loops * stringcounts) strings will be tested. */
constexpr int loops = 1;
 
/* tchar */
using tchar = wchar_t;
 
static void escape( void * p )
{ asm volatile( "" : : "g"(p) : "memory" ); }
 
using tstring = ::std::basic_string< tchar >;

static tchar first_unique_char_in( tstring const & s )
{ ::std::unordered_multiset< tchar >const chars( begin( s ), end( s ));
for( tchar const ch : s )
if( chars.count( ch )== 1 )return ch;
return tchar{ 0 }; }
 
double loopstat_sum;
double loopstat_count;
double innerstat_sum;
double innerstat_count;
 
static tchar first_unique_char_of( tstring & s )
{ long long const z = s.size();
s.push_back( 0 ); /* add sentinel */
long long i = 0; /* no-defined-overflow optimization */
auto const zs = s.size();
for( i = 0; i != z; ++i )
{ tchar const ch = s[ i ];
s[ z ]= ch; /* set sentinel */
bool found = false;
long long j = 0; /* no-defined-overflow optimization */
for( j = 0; ; ++j )
{ if( s[ j ]== ch && j != i ){ found = true; break; }}
::innerstat_sum += j; ::innerstat_count += 1;
if( j == z )found = false; /* sentinel was found */
if( !found )
{ ::loopstat_sum += i; ::loopstat_count += 1;
s.pop_back(); /* remove sentinel */
return ch; }}
s.pop_back(); /* remove sentinel */
::loopstat_sum += i; ::loopstat_count += 1;
return tchar{ 0 }; }
 
static void fill( ::std::vector< tstring >& v )
{ static const char set[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz !";
unsigned long long z = ::zz;
for( int i = 0; i < stringcount; ++i )
{ tstring s{}; s.reserve( z + 10L );
for( unsigned long long c {}; c < z; ++c )
{ switch( fillmode )
{ case 0: s.push_back( set[ rand() %( sizeof( set )- 1 )] ); break;
case 1: s.push_back
( static_cast< unsigned long long >( rand() )*
static_cast< unsigned long long >( RAND_MAX )*
static_cast< unsigned long long >( RAND_MAX )*
static_cast< unsigned long long >( RAND_MAX )+
static_cast< unsigned long long >( rand() )*
static_cast< unsigned long long >( RAND_MAX )*
static_cast< unsigned long long >( RAND_MAX )+
static_cast< unsigned long long >( rand() )*
static_cast< unsigned long long >( RAND_MAX )+
static_cast< unsigned long long >( rand() )); break;
case 2: s.push_back( c ); break;
case 3: s.push_back( 0 ); break;
default: abort(); break; }}
v.push_back( s ); }}
 
int main ()
{ time_t rawtime;struct tm * timeinfo;
::std::srand( static_cast< unsigned int >( ::std::time( nullptr )));
for( fillmode = 0; fillmode < 4; ++fillmode )
{ ::std::cout << "\nfillmode = " << fillmode <<
( fillmode == 0 ? "filling with set of commonly used characters" :
fillmode == 1 ? "filling with wide range of random numbers" :
fillmode == 2 ? "filling with incrementing characters 1, 2, 3 ..." :
"filling with the character 0" )<< '\n';
::std::cout.flush();
for( int i = 0; i < loops; ++i )
{ ::std::vector< tstring >v;
::fill( v );
/* ::std::time( &rawtime );
timeinfo = ::std::localtime ( &rawtime );
::std::cout << "filled "s << ::std::asctime( timeinfo ) << '\n'; */
{ tstring::size_type l = 0; unsigned long long c = 0;
for( auto const & s: v )
{ l += s.length(); ++c; }
::std::cout << "average length of a string = " <<
( static_cast< long double >( l )/static_cast< long double >( c ))
<< '\n'; }
::std::cout.flush();
if( verify )for( auto & s: v )
if( first_unique_char_in( s )!= first_unique_char_of( s ))
{ ::std::cerr << "verification failed.\n"; exit( 99 ); }
 
/* ::std::time( &rawtime );
timeinfo = ::std::localtime ( &rawtime );
::std::cout << "asserted "s << ::std::asctime( timeinfo ) << '\n'; */
{ ::std::chrono::high_resolution_clock::time_point t1;
::std::chrono::high_resolution_clock::time_point t2;
::loopstat_sum = 0; ::loopstat_count = 0;
::innerstat_sum = 0; ::innerstat_count = 0;
decltype( t2 - t1 )sum{};
t1 = ::std::chrono::high_resolution_clock::now();
escape( &t1 );
for( auto & s: v )
{ auto result = first_unique_char_of( s );
escape( &result ); }
t2 = ::std::chrono::high_resolution_clock::now();
escape( &t2 );
sum += t2 - t1;
::std::cout << "runtime of first_unique_char_of = " << ( sum ).count() << '\n';
::std::cout.flush();
::std::cout << "average number of outer loops = " << ::loopstat_sum / ::loopstat_count << '\n';
::std::cout << "average number of inner loops = " << ::innerstat_sum / ::innerstat_count << '\n';
/*::std::time( &rawtime );
timeinfo = ::std::localtime ( &rawtime );
::std::cout << "done dtf "s << ::std::asctime( timeinfo ) << '\n';*/ }
if( measure_in )
{ ::std::chrono::high_resolution_clock::time_point t1;
::std::chrono::high_resolution_clock::time_point t2;
decltype( t2 - t1 )sum{};
t1 = ::std::chrono::high_resolution_clock::now();
escape( &t1 );
for( auto const & s: v )
{ auto result = first_unique_char_in( s );
escape( &result ); }
t2 = ::std::chrono::high_resolution_clock::now();
escape( &t2 );
sum += t2 - t1;
::std::cout << "runtime of first_unique_char_in = " << ( sum ).count() << '\n';
::std::cout.flush();
/*::std::time( &rawtime );
timeinfo = ::std::localtime ( &rawtime );
::std::cout << "done dti "s << ::std::asctime( timeinfo ) << '\n'; */ }
}}}
"Adam C. Emerson" <azure@fox.blue>: Feb 03 01:09AM


> Why do I have to provide a default constructor? As the objects are
> being inserted into the set they can be compared and put in the tree
> structure implementing the set.
[snip]
 
Sir,
 
Look at the first constructor here:
 
http://en.cppreference.com/w/cpp/container/set/set
 
And the answer to your question should be made clear.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 03 04:27AM +0100

On 02.02.2017 21:37, Joseph Hesse wrote:
> s.insert(x3);
 
> return 0;
> }
 
Well, the main /technical/ problem is that the std::set default
constructor, used above, in order to provide a default comparator
object, requires that the comparator type is default-constructible.
 
But you don't need to use an instance of your class X directly as
comparator.
 
You can just define an ordering of X instances by defining an
`operator<`, and then use the default comparator of `std::set`:
 
 
[code]
#include <set>
 
class X
{
private:
int a; // only used to make xobj's function objects
public:
//X() : a(1) {} // WORKS FINE WITHOUT THIS
X(int b) : a(b) {}
 
// following makes instances of X function objects
auto operator()( X const& x1, X const& x2 ) const
-> bool
{ return x1.a < x2.a; }
 
// This defines an ordering of X instances, that's all that
// std::set needs. The awkwardness reflects a design issue.
friend
auto operator<( X const& a, X const& b )
-> bool
{ return a.operator()( a, b ); }
};
 
int main()
{
X x1{ 3 };
X x2{ 4 };
X x3{ 5 };
 
std::set<X> s; // No 2nd X because "<" is defined for X objects.
s.insert( x1) ;
s.insert( x2 );
s.insert( x3 );
}
[/code]
 
 
• • •
 
A good general guideline is to separate concerns as much as practically
possible.
 
The X class was and is, apparently, responsible for too much, too many
concerns. I have no idea what the `a` value is all about, but a function
object class that is only about comparing instances of itself seems not
very useful, so presumably the `a` is also about something else. And the
comment on the operator definition, "makes instances", indicates some
third purpose, that just didn't make it all the way out to the trimmed
example that you posted.
 
So, generally, separate concerns.
 
There's even a Wikipedia page about it: <url:
https://en.wikipedia.org/wiki/Separation_of_concerns>.
 
The `operator<` defined above is an example. It does one single job. And
it technically moves that job out of the class (except for convenience
the definition could have been placed outside the class).
 
 
Cheers & hth.,
 
- Alf
"Öö Tiib" <ootiib@hot.ee>: Feb 03 05:20AM -0800

On Thursday, 2 February 2017 22:37:34 UTC+2, Joseph Hesse wrote:
 
> Why do I have to provide a default constructor? As the objects are
> being inserted into the set they can be compared and put in the tree
> structure implementing the set.
 
You don't need to provide default constructor. I explain below inline
with code.
 
 
> Thank you,
> Joe
 
You are welcome.
 
> int a; // only used to make xobj's function objects
> public:
> X() : a(1) {} // WON'T WORK WITHOUT THIS
 
Replace above with:
// X() : a(1) {} // WILL WORK WITHOUT THIS

 
 
> int main()
> {
> std::set<X, X> s; // 2nd X because instances of X are function objects
 
Replace above with:
 
// non-default constructible comparator needs instance supplied
std::set<X, X> s(0);
 
 
> s.insert(x3);
 
> return 0;
> }
 
Should compile and run.
Brett Dong <brett.browning.dong@gmail.com>: Feb 03 11:18AM +0800

On 2017/2/2 6:57, Rick C. Hodgin wrote:
> begins with the first step.
 
> Thank you,
> Rick C. Hodgin
 
Of course this post has nothing to do with C++ programming language.
Barry Schwarz <schwarzb@dqel.com>: Feb 02 09:21PM -0800

On Fri, 3 Feb 2017 11:18:21 +0800, Brett Dong
 
>On 2017/2/2 6:57, Rick C. Hodgin wrote:
 
<snip>
 
>Of course this post has nothing to do with C++ programming language.
 
But that didn't stop you from reposting the whole thing so those of us
who have killfiled the author could see it despite our best efforts
not to.
 
--
Remove del for email
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 03 05:29AM +0100

On 02.02.2017 21:57, Richard wrote:
 
>> Can anyone explain me what the "owner-concept" or "GSL" of C++17 is?
 
> Are you talking about the Guideline Support Library?
> <https://github.com/Microsoft/GSL>
 
Hm, I see code like this:
 
 
// final_act allows you to ensure something gets run at the end of a scope
template <class F>
class final_act
{
public:
explicit final_act(F f) noexcept : f_(std::move(f)), invoke_(true) {}
 
final_act(final_act&& other) noexcept : f_(std::move(other.f_)),
invoke_(other.invoke_)
{
other.invoke_ = false;
}
 
final_act(const final_act&) = delete;
final_act& operator=(const final_act&) = delete;
 
~final_act() noexcept
{
if (invoke_) f_();
}
 
private:
F f_;
bool invoke_;
};
 
 
I don't like that they've renamed ScopeGuard to something else. More
names to learn, not easily recognized, and the new name points in a
misleading direction.
 
I don't like that there's no reference to Marginean and Alexandrescu.
 
I don't like that they've removed the trivial but important `dismiss`
method. That's like wanton destruction, in the usual Microsoft style.
Apparently the authors don't understand the concept here. :(
 
I don't like that they've simply added `noexcept` to the destructor,
apparently just for conformity's sake. For example, `final_act`
augmented with a `dismiss` method can't be used to implement a
transaction, when roll-back can fail, without an elaborate scheme for
propagating an exception across the `noexcept` barrier. The original
ScopeGuard wasn't perfect in that way, either, because it just
unreasonably /swallowed/ exceptions from the cleanup function (as I
recall Andrei didn't see that as a problem at all when I asked him about
it), but at least it didn't default to crashing the program, preventing
larger scale cleanup, on a little local cleanup failure.
 
If this library becomes widely used, as is possible, then it will again
be that competent people can't just rely on the common infra-structure
but must re-create parts of it to have something practically useful.
 
 
[snip]
 
Cheers!,
 
- Alf
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: