- Microsoft dropped the ball... - 18 Updates
- Why should a "c" programmer learn c++ ? (V2) - 5 Updates
- Are there any asm-instructions to support OOP - 2 Updates
| Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 17 12:34AM +0100 > { > return 2; > } This is not "per the bug report". You picked these, possibly unaware that they are useless examples. The bug report and no explicit a and b. > std::cout << a() << b(); > std::cout << std::endl; > // Reports 12 As it must. Your a() and b() can't illustrate the problem, because it's about side effects. > std::cout << (a() << b()); > std::cout << std::endl; > // Reports 4 The issue is about operator precedence and parentheses. > // C way > printf("%d\n", a() << b()); The C equivalent of the original posted code is (depending on the return type) printf("%d,%d\n", a(), b()); and suitable examples to show a potential sequencing bug might be int a(void) { fprintf(stderr, "a called\n"); return 1; } int b(void) { fprintf(stderr, "b called\n"); return 2; } (writing (void) because these are being used in C and C++ code). Prior to C++17, std::cout << a() << "," << b() << "\n"; could produce either a called b called or b called a called on stderr, though always 1,2 on stdout. C++17 added a rule that in E1 << E2, E1 is sequenced before E2. That is a statement about when any side effects must be seen to occur, and is not about operator precedence. In C, the "problem" (if one chooses to see it as one), persists to this day. All function argument evaluations are sequenced before the call, but are unsequenced with respect to each other. In this case, a and b must both be called and any side effects completed before the call to printf, but a and b can be called in either order. > } > I only have Visual Studio 2003 on this computer, and it reports 12, 4, > and 4. It should be reporting 4, 4, and 4, in C++17. Your example a() and b() can't show the issue. > C gets it right. C++ does too with a little help from its friends > (parenthetically speaking of course). It's not about parentheses. >> Right... > See how easy it is to misunderstand something? You do it > effortlessly. You make it look easy. :-) And here's a smilie. So was the above just all a big joke too? No matter; maybe someone else will get something from reading it. > Actually, this would be a great teaching moment for Bonita. It > demonstrates that adding an extra set of parenthesis now and then > removes real-world code issues. Parentheses can't help. -- Ben. |
| "Rick C. Hodgin" <rick.c.hodgin@gmail.com>: Sep 16 08:08PM -0400 On 9/16/20 7:34 PM, Ben Bacarisse wrote: > ...C++17 added a rule that in E1 << E2, E1 is sequenced before E2. Yeah. My mistake. I misunderstood. I thought the issue to be the overloaded << operator working on the result of a() shift left b(), compared to outputting a() and b() as values to stdout. If I understand the issue correctly now, that e1 is called before e2 ??, then I think that's only natural. I would find the fact that it didn't work that way before to be surprising. And if I still misunderstand then I really don't care. The length of this conversation is already way out of proportion to my level of interest in it. -- Rick C. Hodgin |
| "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 16 11:07PM -0700 On 9/16/2020 12:16 PM, Mr Flibble wrote: > Just submitted a VS2019 bug report (C++17 non-conformance): > std::cout << a() << "," << b(); // a() is not sequenced before b() Really? I have 2017 installed. Its just that I never use function calls in cout sequences. |
| "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 16 11:10PM -0700 On 9/16/2020 12:57 PM, Rick C. Hodgin wrote: >> How so? C has weaker rules about expression evaluation sequencing than >> C++ does and none that fix this issue with sequencing IO operations. > You wouldn't write IO in that way in C, so the problem goes away. Have to admit that I prefer printf over cout in a lot of cases. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 17 07:05AM > If I understand the issue correctly now, that e1 is called before > e2 ??, then I think that's only natural. I would find the fact that > it didn't work that way before to be surprising. Actually it's not really "natural" because it's a very widely known fact (and warning) that if you do something like: someFunction(a(), b(), c()); the order in which those functions a(), b() and c() are called is not guaranteed. The compiler is free to call them in any order it wants (and, indeed, different compilers *do* call them in different orders in practice. It's not just theoretical.) Likewise if you do something like this: x = a() + b() + c(); the order in which of those function calls are done is not guaranteed. Once again the compiler is free to do them in whatever order it wants. Because of this, it's not so obvious why in std::cout << a() << b() << c(); those functions couldn't likewise be called in whatever order the compiler wants (as long as the operator<<() function calls are done in the specified order, which is independent of the order in which the function calls a(), b() and c() are done.) I suppose C++17 decided to make an exception in this case, and mandated that with such operators as << the order in which the parameters are evaluated should be fixed. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 17 07:11AM > Have to admit that I prefer printf over cout in a lot of cases. There are other cases where std::cout is just more practical. Suppose, for example, that you wanted to implement a simpler printing function, for example just to print some debugging messages, that you can call like: int a = 5; MyCustomClass b; std::string str = "hello"; dPrint("The value of a is ", a, ", that of b is ", b, ", and str is \", str, "\"\n"); Implementing such a function is perfectly possible, even easy. But it's easy only if you use std::cout in its implementation. Not so easy if you wanted to use std::printf instead. |
| "Öö Tiib" <ootiib@hot.ee>: Sep 17 12:15AM -0700 On Thursday, 17 September 2020 09:10:48 UTC+3, Chris M. Thomasson wrote: > >> C++ does and none that fix this issue with sequencing IO operations. > > You wouldn't write IO in that way in C, so the problem goes away. > Have to admit that I prefer printf over cout in a lot of cases. Both are good enough for command line human I/O but for something more demanding ... neither is efficient nor convenient enough. Both have some kind of laughably insufficient and cryptic i18n support in those that totally degrades the performance. That cout even makes virtual calls! :D Consider: we receive compile time warnings about printf format specifiers from several compilers. Yet those compile it into program that parses that format string run time again. Why? Because of that sub-par localisation garbage that does nothing else but causes confusion sometimes. The gettext library for example totally rewrites it to achieve something useful for i18n. |
| David Brown <david.brown@hesbynett.no>: Sep 17 09:21AM +0200 On 17/09/2020 08:07, Chris M. Thomasson wrote: >> std::cout << a() << "," << b(); // a() is not sequenced before b() > Really? I have 2017 installed. Its just that I never use function calls > in cout sequences. Given that the sequencing was unspecified until C++17, then it is a good idea to avoid << chains that rely on the sequencing. The change was made in C++17 because some people /did/ write code that had << chains with function calls, believing that the order of evaluation (and side-effects) was guaranteed. |
| "Öö Tiib" <ootiib@hot.ee>: Sep 17 12:41AM -0700 On Thursday, 17 September 2020 10:11:53 UTC+3, Juha Nieminen wrote: > Implementing such a function is perfectly possible, even easy. But > it's easy only if you use std::cout in its implementation. Not so easy > if you wanted to use std::printf instead. Some languages have solution to that problem as part of string class. For example Swift: var text = "The value of a is \(a) that of b is \(b) and str is \"\(str)\"\n" Swift is compiling to LLVM and performs similarly to C and C++. |
| Bo Persson <bo@bo-persson.se>: Sep 17 10:26AM +0200 On 2020-09-17 at 08:07, Chris M. Thomasson wrote: >> std::cout << a() << "," << b(); // a() is not sequenced before b() > Really? I have 2017 installed. Its just that I never use function calls > in cout sequences. Function calls are fine, unless calling one function affects the result of the other function. Having such a combo is a bit unstable, not only for I/O. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 17 11:34AM > Because of that sub-par localisation garbage that does nothing > else but causes confusion sometimes. The gettext library for > example totally rewrites it to achieve something useful for i18n. I don't really understand why the C++ standard library (and possibly the C standard library, where C++ "inherited" it) decided to go with those stupid localization complications. It has bitten me in the posterior in the form of the precise format that std::atof() accepts depending on the locale (more precisely, the exact character used as the decimal point depends on the locale), which made it completely useless for my purposes (because I wanted to parse floating point values in a fixed format). No wonder they added the from_chars() functions to address this problem. |
| Leo <usenet@gkbrk.com>: Sep 17 06:12PM +0300 On 9/17/20 10:41 AM, Öö Tiib wrote: > Swift is compiling to LLVM and performs similarly to C and C++. In what world is Swift performing similarly to C and C++? Maybe badly written C or Swift that no longer looks like Swift. For 99% of the code mortals will come across, Swift performs much much worse than C or C++. |
| "daniel...@gmail.com" <danielaparker@gmail.com>: Sep 17 08:26AM -0700 On Thursday, September 17, 2020 at 7:34:38 AM UTC-4, Juha Nieminen wrote: > I don't really understand why the C++ standard library (and possibly the > C standard library, where C++ "inherited" it) decided to go with those > stupid localization complications. Agreed. Localization is about formatting, basic conversion should be into canonical forms. C++ streams have an imbue modifier, that allows you to set the locale to the "C" (minimal) locale. But libraries that have to compete with others can't use streams for text/numeric conversion because that would be too slow compared to their competitors. So they use the _l (locale independent) C functions where they exist, and where they don't, they jump through hoops to reverse the affects of localization from sprintf, sscanf and strtod. Serialization libraries that make claims about speed generally avoid the C++ streams and the C functions altogether, and provide their own implementations of number to text and text to number, usually with inexact float parsing. > No wonder they added the from_chars() functions to address this problem. Regarding characters, C++ has char, signed char, unsigned char, char8_t, char16_t, char32_t, and wchar. And C++ has the notion of generics. So I don't understand why a function named from_chars is limited to taking a sequence of char. Or why a function named to_chars is limited to writing into a range of char. Daniel |
| "Öö Tiib" <ootiib@hot.ee>: Sep 17 09:16AM -0700 On Thursday, 17 September 2020 18:12:24 UTC+3, Leo wrote: > written C or Swift that no longer looks like Swift. > For 99% of the code mortals will come across, Swift performs much much > worse than C or C++. Cite? |
| Melzzzzz <Melzzzzz@zzzzz.com>: Sep 17 05:01PM >> std::cout << a() << "," << b(); // a() is not sequenced before b() > Really? I have 2017 installed. Its just that I never use function calls > in cout sequences. If functions don't have side effect, then no problem. In haskell almost evry function is pure, that is you can't predict when they are evaluated ;) -- current job title: senior software engineer skills: c++,c,rust,go,nim,haskell... press any key to continue or any other to quit... U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec Svi smo svedoci - oko 3 godine intenzivne propagande je dovoljno da jedan narod poludi -- Zli Zec Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi bili naoruzani. -- Mladen Gogala |
| Leo <usenet@gkbrk.com>: Sep 17 08:05PM +0300 On 9/17/20 7:16 PM, Öö Tiib wrote: >> For 99% of the code mortals will come across, Swift performs much much >> worse than C or C++. > Cite? How can I prove that Swift performs similarly to C, I don't even know Swift. You need to write benchmarks so C/C++/Rust developers can fix the performance bugs that make them as slow as Swift. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 17 06:29PM >> Cite? > How can I prove that Swift performs similarly to C, I don't even know > Swift. You don't know Swift, but you know it performs much worse? Based on what? |
| "Öö Tiib" <ootiib@hot.ee>: Sep 17 11:45AM -0700 > Regarding characters, C++ has char, signed char, unsigned char, char8_t, > char16_t, char32_t, and wchar. And C++ has the notion of generics. Some are historically there but newer ones seem to be added to make things more confused. All of those besides char are anyway less useful than char and nothing helps it. > So I don't understand why a function named from_chars is limited > to taking a sequence of char. Or why a function named to_chars is > limited to writing into a range of char. Best performant storage types for manipulating texts are std::string, std::vector<char>, std::array<char,N> and raw char array. Also std::string_view is useful as it can refer to contents of any of those types. It is like most useful encoding is BOMless UTF-8 encoding (or its proper subset ASCII) that also works great in those containers. Everything else is just kook stuff, family of EBCDIC. Therefore performance features from_chars and to_chars are doing the obvious ... namely reading and writing chars in most performant containers of text. The only thing left dim there is about INF and NAN otherwise those are all what is needed. |
| Anton Shepelev <anton.txt@gmail.com>: Sep 17 02:47AM +0300 Ben Bacarisse to Anton Shepelev: > > int **p[100]; > It's not really the point of the thread, but that's an > array of pointers to pointers to int. I thank you and others for the discussion of this problem, and Olcott for offering it. My answer is doubly wrong: 1. It assumes the wrong operator precedence. 2. It ignores the difference between pointer and array types. -- () ascii ribbon campaign -- against html e-mail /\ http://preview.tinyurl.com/qcy6mjc [archived] |
| Juha Nieminen <nospam@thanks.invalid>: Sep 17 06:56AM > (Yes, I cheated and used cdecl.) > The distinction between arrays and pointers is muddy enough without > glossing over the very real differences. What I was thinking with my original question is the syntax to pass, for example, a pointer to a two-dimensional array as a parameter to a function. Typically if you just want to give a pointer to an array to a function, the function would declared for example like: void foo(int* values, size_t valuesAmount); But what if you wanted the function to take a pointer to a *two-dimensional* array, where the second dimension is known at compile time (eg. 100)? Surprisingly few C and C++ programmers, even experienced ones, know how to do this. Many are not even aware that such syntax is *possible* in the first place. Many of the ones who know that it's theoretically possible don't know or remember the exact syntax. The syntax is, of course: void foo(int (*values)[100], size_t valuesAmount); (That odd unusual syntax, with those strange parentheses, can be confusing. Beginner C/C++ programmers might even think that the parentheses are redundant and do nothing, similarly to how they do nothing in expressions like "1+(2)" and "return (value)".) Of course the array could have even more dimensions. For example taking a pointer to a three-dimensional array of size [x][100][200] would be likewise: void foo(int (*values)[100][200], size_t valuesAmoun); |
| Bart <bc@freeuk.com>: Sep 17 11:38AM +0100 On 17/09/2020 07:56, Juha Nieminen wrote: > first place. Many of the ones who know that it's theoretically possible > don't know or remember the exact syntax. The syntax is, of course: > void foo(int (*values)[100], size_t valuesAmount); Technically that's a pointer to a one-dimensional array - note that the type uses one "*", the pointer, and one pair of "[]", the array. That's typical C idiom for passing any array: the first dimension or array part is reduced to a pointer. That is, to pass a T[] type (where T might itself be U[]), a T* type is passed. However with multidimensional arrays, often the dimensions other than the first are not fixed. The compiler needs to know them in order to know how to do perform the indexing operations. So if the 100 is instead 'm', another parameter, the declaration might be: void foo(size_t m, int (*values)[m], size_t n); (m needs to precede the array declaration). However this is not my expertise; there are lots of such little known and surprisingly high level aspects to C. This construction will pass, I believe, a pointer to a FLAT 2D array, where all elements are in a single block of memory. Or, as you've written it, a pointer to the first ROW of the 2D array. My own language doesn't support passing pointers to flat 2D arrays unless the lower dimensions are fixed (only the first can be open). It does suppport non-flat 2D arrays, where the first array level is a set of pointers each of which points to a row of the array: proc foo(ref[]ref[] int values) # or: proc foo([]ref[] int &values) In both cases, any derefs can be omitted so that the array is accessed as: values[i,j] instead of needing: values^[i]^[j] # 1st values[i]^[j] # 2nd Of course, C can define such an array as well, I just have no idea how! However, my translation tool tells me the C equivalent would be: void foo(int (*(*values)[])[]); And accesses might be: (*(*values)[i])[j]; Although, knowing C, you can probably plug in any combination of * and [], so long as there are 4 in total, and the compiler will not complain, especially if those [] contain bounds. |
| Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 17 05:11PM +0100 > What I was thinking with my original question is the syntax to pass, > for example, a pointer to a two-dimensional array as a parameter to > a function. That's what you call it, but I would refer to passing a pointer to an element of such an array (when I remember to be careful). Being 2D, that element is itself an array, but that does not alter the term I'd use. Without context, your terminology is open to misinterpretation. > Typically if you just want to give a pointer to an array to a function, > the function would declared for example like: > void foo(int* values, size_t valuesAmount); We may, casually, say that's a pointer to an array, but it isn't really. It's a pointer to an int which may be located somewhere in an array, but it need no be. > first place. Many of the ones who know that it's theoretically possible > don't know or remember the exact syntax. The syntax is, of course: > void foo(int (*values)[100], size_t valuesAmount); It's directly analogous to the syntax for function pointers, so I don't find it hard to remember. C declarators (the bit of the type with the stars and the []s and so on) use a little expression syntax. You sometimes need parentheses to alter the binding: *x[...] :: array of pointer (*x)[...] :: pointer to array *x(...) :: function returning pointer (*x)(...) :: pointer to function. Top tip: a cast operator is just a single declaration with the name missing and the put in parentheses. So if you need the cast operator to convert to a pointer to an array of 10 ints, it's just (int (*)[10]) ... -- Ben. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 17 06:27PM > missing and the put in parentheses. So if you need the cast operator to > convert to a pointer to an array of 10 ints, it's just > (int (*)[10]) ... Another pro tip, which helps understand the sometimes weird-appearing behavior of the 'typedef' keyword: To make a type alias, just write the type definition exactly is you were defining a variable of that type, and slap the keyword 'typedef' in front of it: Now instead of the name you wrote being the name of a variable, it's the name of the type alias. That simple idea helps clearing up a lot of confusion about how 'typedef' works. So, the simplest example is of course: int anInteger; // variable definition typedef int AnInteger; // type definition Doesn't look like much of a tip with that example. However, the ingeniousness of the tip becomes clearer when you consider how to declare type aliases for more complicated cases. For instance, how to declare a type alias that means "a pointer to a function taking an int and returning an int"? Well, first thing how you would define a *variable* of that type: int (*funcPtr)(int); Now just slap 'typedef' in front of it, and you're done: typedef int (*FuncPtr)(int); Now 'FuncPtr' is a type alias for a function pointer of that type. This explains the sometimes obscure-looking typedef syntax. |
| "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 16 10:52PM -0700 On 9/16/2020 4:38 AM, Öö Tiib wrote: >> compilers have no implementation of it. People want the benefits now, >> not some time in the future maybe perhaps, if the program is recompiled. > Are you talking about Linear-Tape-Open or Link-Time-Optimisation? The latter. I used to have to turn it off before threading and atomics became standard. |
| David Brown <david.brown@hesbynett.no>: Sep 17 09:14AM +0200 On 16/09/2020 23:24, Jorgen Grahn wrote: >> compilers have no implementation of it. > Doesn't gcc's support (-flto) count? I haven't used it, but as I > understand it, it has been used by real software for years. I have no idea why Juha thinks no compilers have LTO. I suspect gcc -flto (and clang's equivalent) is relatively little used in practice. For one thing, it is less well known - a fair number of C and C++ programmers seem to be unaware of even the basic "-O2" style of optimisation flags. Another is that it can cause trouble for debugging, since it re-arranges code over such a wide scale (imagine single-stepping or using breakpoints on LTO code). For embedded systems, it can be a challenge to make sure you've kept everything you need, such as interrupt functions, vector tables, and other data. These add complications to the development process. And for larger code bases, where LTO would often make the biggest difference, you can have massive processor and memory requirements for the linking part. If you have a 1000 modules in your project, and are using non-LTO builds with "make", then when you change one file and type "make" you compile one file and link everything. With a big project, the link becomes the major bottleneck - it takes longer than any one compile, and can't be done as a parallel job. But with LTO this gets amplified by orders of magnitude - now compilation can be faster as object code is not generated, but even after changing just one source file, the LTO link means effectively re-compiling everything in the project. The parallelisation and resource usage of LTO builds has been a major focus for gcc in recent versions, and has (AFAIUI) improved significantly. But you still want a lot of memory and a lot of cores to use it! gcc has also had "-fwhole-program" for a long time, which worked fine for small code (you had to compile all your modules in one shot). And some compilers for small embedded cores have had link-time optimisation (often known as "omnipotent compilation" for decades. |
| 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