- Header-only C++, what is the point? In C this would be a obvious mistake - 16 Updates
- The first non-duplicated letter in a string - 1 Update
- std::set of X requires a default constructor for X - 3 Updates
- Why study the Bible? What can we learn? - 2 Updates
- "owner-concept" or "GSL" ...? - 1 Update
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:
Post a Comment