- The unintended consequences of the 'auto' keyword - 18 Updates
- Lamda function-pointer wrapping - 3 Updates
- "C++ proposal dismisses backward compatibility" - 1 Update
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 03:44AM +0200 On 15.04.2020 23:03, James Kuyper wrote: > One key reason why they tend to be unproblematic is that the integral > promotions, as a matter of deliberate design, never change the value of > an expression, only it's type. The purpose of an `operator""i16` is to control the type. So with all those words you're ignoring the main aspect. Since you ask for an example, `foo( -42i16 )` can do the entirely wrong (unintended) thing, for example if foo copies a value into a memory area representing a file header, and advances a write position. It could even be that foo is so badly designed that it does slightly different but conceptually similar things for `int` and `int16_t`. I recently had experience with the FLTK GUI library, used in Bjarne's intro book, and it does slightly different things for `double` versus `int` coordinates in graphics, e.g. whether it involves transformation. When one deals with libraries like that controlling the type is key. There is another important aspect, that of the language being predictable, no big surprises, and not having meaningless constructs. Getting type `int` out of `int16_t(-42)` renders it meaningless. So that's a design level bug in the scheme for user defined literals. - Alf |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 03:49AM +0200 On 16.04.2020 03:44, Alf P. Steinbach wrote: > predictable, no big surprises, and not having meaningless constructs. > Getting type `int` out of `int16_t(-42)` renders it meaningless. So > that's a design level bug in the scheme for user defined literals. Sorry, paste error, should be "int` out of `-42i16`". |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 15 07:40PM -0700 On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote: > >> On 15.04.2020 21:54, James Kuyper wrote: > >>> On 4/15/20 2:58 PM, Alf P. Steinbach wrote: > >>>> On 15.04.2020 17:03, David Brown wrote: ... > Since you ask for an example, `foo( -42i16 )` can do the entirely wrong > (unintended) thing, for example if foo copies a value into a memory area > representing a file header, and advances a write position. Yes, but that's nothing new. Given constexpr int16_t negfortytwo = -42; negfortytwo has a type that is very well-defined, but despite that fact, you'll have exactly the same problems with negfortytwo that you would with -42i16. It might be annoying that the ability to specify user-defined literals doesn't solve this problem, but its not a disaster, it's something that every experienced C or C++ programmer has been dealing with for as long as they've been using those languages. > intro book, and it does slightly different things for `double` versus > `int` coordinates in graphics, e.g. whether it involves transformation. > When one deals with libraries like that controlling the type is key. The integral promotions never result in a floating point type. The usual arithmetic conversions do so only if one of the two types involved is a floating point type. It strikes me as quite reasonable that int coordinates and double coordinates do things somewhat differently - there's things that you can't do with int coordinates because they have a much larger granularity. > predictable, no big surprises, and not having meaningless constructs. > Getting type `int` out of `int16_t(-42)` renders it meaningless. So > that's a design level bug in the scheme for user defined literals. It would be more of a surprise, at least to experienced programmers, if the integral promotions don't occur just because user defined literals are involved. It would be slightly less confusing if the integer promotions were simply removed completely, rather than setting up a special exemption from them for user define integer literals. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 05:36AM +0200 On 16.04.2020 04:40, James Kuyper wrote: > negfortytwo has a type that is very well-defined, but despite > that fact, you'll have exactly the same problems with negfortytwo > that you would with -42i16. No. You fail to understand that if `foo` depends on the type of the argument, then it depends on the type of the argument. The dependency I sketched was how many bytes it would write and advance a write position, but there are infinitely many other reasonable such dependencies in C++. > its not a disaster, it's something that every experienced C or > C++ programmer has been dealing with for as long as they've been > using those languages. No, they haven't. It gives the impression that you have very little experience. I know that to be probably false, so it's baffling. >> `int` coordinates in graphics, e.g. whether it involves transformation. >> When one deals with libraries like that controlling the type is key. > The integral promotions never result in a floating point type. That's an irrelevant observation. > reasonable that int coordinates and double coordinates do things > somewhat differently - there's things that you can't do with int > coordinates because they have a much larger granularity. I find it unreasonable. To me it's just an idiotic design. The rationale of overloads is that they do the conceptually the same. When that isn't the case, a decent design uses different names. And namespaces. > confusing if the integer promotions were simply removed > completely, rather than setting up a special exemption from them > for user define integer literals. Rather than special-casing the existing rules, as you seem to imagine, a better design of user-defined literals would have removed the dependency on a language feature (the sign operators) that renders many expressions meaningless. - Alf |
David Brown <david.brown@hesbynett.no>: Apr 16 10:02AM +0200 On 16/04/2020 04:40, James Kuyper wrote: > its not a disaster, it's something that every experienced C or > C++ programmer has been dealing with for as long as they've been > using those languages. No, it's different. Let me show this with a program: #include <iostream> #include <stdint.h> using namespace std; void foo(int16_t x) { cout << "int16_t " << x << "\n"; } void foo(int32_t x) { cout << "int32_t " << x << "\n"; } void foo(int64_t x) { cout << "int64_t " << x << "\n"; } int16_t operator"" _i16(unsigned long long x) { return x; } class Int16_t { private : int16_t x_; public : constexpr Int16_t(int16_t x) : x_(x) {}; constexpr Int16_t operator-() { return -x_; } constexpr operator int16_t() { return x_; } }; Int16_t operator"" _I16(unsigned long long x) { return x; } constexpr int16_t negfortytwo_a = -42; const int16_t negfortytwo_b = -42; int main() { foo(123); foo((int16_t) 123); foo(-(int16_t) 123); foo((int16_t) -123); foo(42_i16); foo(-42_i16); foo(43_I16); foo(-43_I16); foo(negfortytwo_a); foo(negfortytwo_b); } The output of this is: int32_t 123 int16_t 123 int32_t -123 int16_t -123 int16_t 42 int32_t -42 int16_t 43 int16_t -43 int16_t -42 int16_t -42 None of this is a surprise when you know the integer promotion rules, and when you understand that integer literals are always non-negative - the minus sign is not part of the literal. But having the minus sign not be part of the literal is counter-intuitive, and goes against common mathematics. The number -42 is a negative number - it is not something we construct by taking a positive number and negating it (though in mathematics we know the result is the same). Why would someone want an "_i16" user defined literal at all? Surely it is to construct something specifically known to by of type int16_t, as the value is not changed. And since it is going to be promoted in any arithmetic context, you are going to want it in a context where type is critical, such as for function overloading. It might not be a /surprise/ that -42_i16 is an int, but it is certainly going to be annoying and useless. The answer here - as usual in C++ - is if you don't like the current behaviour of a type, make a class that you /do/ like. It is possible to make your own classes that don't promote - the Int16_t above is a start. I can think of a couple more situations where integer promotion can cause trouble, which could be solved by non-promoting types: uint16_t a = 60000; uint16_t b = a * a; On a 16-bit int machine, "a * a" is done as unsigned arithmetic, and gives you 41984 as the result. On a 32-bit int machine, the operands are promoted to "int", you are calculating 60000 * 60000 in signed integer arithmetic, and it overflows - potentially doing unpleasant things. This is despite writing everything with unsigned types that normally have defined wrapping. And for a somewhat more niche case, on 8-bit systems (like the AVR - the only 8-bit target supported by gcc, and (I think) llvm/clang) you have 16-bit ints. You usually want to do as much as possible using uint8_t or int8_t for efficiency, but the language insists on bumping these up to 16 bit before doing most arithmetic operations. Compilers can do a reasonable job of eliminating some of the excess code, but sometimes non-promoting user types work better. On some 8-bit C compilers for embedded systems, integer promotion is not applied. This results in more efficient code, and more confusion because the code doesn't follow C rules. |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 16 11:55AM -0400 On 4/15/20 11:36 PM, Alf P. Steinbach wrote: > On 16.04.2020 04:40, James Kuyper wrote: >> On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote: >>> On 15.04.2020 23:03, James Kuyper wrote: ... >>>> Could you explain that a little more? Specifically, could you give an >>>> example of how it could cause problems? You've responded to me twice now, and still have not provided an example. Source code demonstrating the problem you're worried about would make your point much clearer than any amount of words could - particular since it's become quite clear that we aren't communicating with each other very well using words. >>> representing a file header, and advances a write position. >> Yes, but that's nothing new. Given >> constexpr int16_t negfortytwo = -42; Sorry - I muddied the waters by incorporating the '-' into that definition. The parallels are clearer in the code below, which handles that differently. >> that you would with -42i16. > No. You fail to understand that if `foo` depends on the type of the > argument, then it depends on the type of the argument. Note: according to n4659.pdf "Literal suffix identifiers (16.5.8) that do not start with an underscore are reserved for future standardization." (20.5.4.3.5). I found this out while testing the code below, which wouldn't compile until I changed i16 to _i16. #include <iostream> #include <typeinfo> int16_t operator"" _i16(unsigned long long x) { return x; } constexpr int16_t fortytwo_16 = 42; template<class T> void foo(T t) { std::cout << typeid(t).name() << ":" << t << "\n"; } int main(void) { foo(fortytwo_16); foo(42_i16); foo(-fortytwo_16); foo(-42_i16); return 0; } ~/Programming/C++/testprog(61) ~/Programming/C++/testprog(60) g++ -std=c++1y -pedantic -Wall -Wpointer-arith -Wcast-align -fno-enforce-eh-specs -ffor-scope -fno-gnu-keywords -fno-nonansi-builtins -Wctor-dtor-privacy -Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual -Wsign-promo literal.cpp -o literal ~/Programming/C++/testprog(62) literal s:42 s:42 i:-42 i:-42 Yes, foo does depend upon the type of the argument. And the integral promotions triggered by the use of unary '-' do change the type. But that's just as much of a problem with -42_i16 as it is with -fortytwo_16. >> using those languages. > No, they haven't. It gives the impression that you have very little > experience. I know that to be probably false, so it's baffling. I've been dealing with the problem shown by my code above for more than 30 years now, and it's not particularly difficult to do so. It predates int16_t - the same problem comes up with "short" and "signed char". It predates C++ - C has the same rule, though they are now called "integer promotions" rather than "integral promotions". If that rule causes some different kind of problem for user-defined literals, you've yet to articulate what that problem is. The clearest way to show the problems you're worrying about would be to show me some code where -42_i16 causes a problem, but replacing it with -fortytwo_16 does not. >>> When one deals with libraries like that controlling the type is key. >> The integral promotions never result in a floating point type. > That's an irrelevant observation. You've complained about a problem supposedly caused by integral promotions, and in the same context, a library that does different things with integral and floating point arguments. If the fact that integral promotions never result in a floating point type is irrelevant to the second part of that complaint, then pray please explain the connection that you do see between your previous complaint and this one. ... > better design of user-defined literals would have removed the dependency > on a language feature (the sign operators) that renders many expressions > meaningless. The use of user-defined literals is dependent on the integral promotions entirely because the operator""() overloads are allowed to return integral types that are smaller than int. The only way to remove that dependency without special casing for user-defined literals would be to prohibit them from returning integer types that are smaller than int. And that would count as special-casing on the return type. It would be far easier for me to have some idea what in the world you're talking about if you would please identify specific clauses in the standard that you think should be changed, and specify how they should be changed. Please provide that actual new wording, not just a description of the wording. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 06:31PM +0200 On 16.04.2020 17:55, James Kuyper wrote: >>>>> example of how it could cause problems? > You've responded to me twice now, and still have not provided an > example. That is a lie. You asked for an example. I gave one. Plonk. - Alf |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 16 02:49PM -0400 On 4/16/20 12:31 PM, Alf P. Steinbach wrote: >> You've responded to me twice now, and still have not provided an >> example. > That is a lie. You asked for an example. I gave one. OK, that wasn't quite right. You gave me something which you called an example: foo(-42i16). However, I didn't consider it an example because you didn't provide any definition for foo(). Without a definition for foo(), I can't figure out how foo(-42i16) would work any differently from foo(-fortytwo_16). With a definition, even if I couldn't figure it out initially, I could at least prove to myself that it does work differently. Having seen it work differently, I'm sure that I would eventually figure out why. |
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Apr 16 11:52AM -0700 >> example. > That is a lie. You asked for an example. I gave one. > Plonk. Alf, I took a quick look at the posts upthread from this one, and I didn't see an example from you. It's utterly implausible that James would lie about something like that, or would have any reason to. It's possible that there's an honest disagreement about what constitutes a valid example, or that he overlooked something. Can you repost the example you're referring to? -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips Healthcare void Void(void) { Void(); } /* The recursive call of the void */ |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 16 03:28PM -0400 On 4/16/20 2:52 PM, Keith Thompson wrote: > reason to. It's possible that there's an honest disagreement about > what constitutes a valid example, or that he overlooked something. > Can you repost the example you're referring to? After the fact, I review his previous posts, and the one immediately after I asked for an example contained this: "Since you ask for an example, `foo( -42i16 )` can do the entirely wrong (unintended) thing, for example if foo copies a value into a memory area representing a file header, and advances a write position." Since no definition for foo() is provided, that didn't qualify as what I would consider a valid example. However, he explicitly labelled it as one, so Alf obviously disagrees. Because I didn't consider it a valid example, I quickly forgot about it, and that's why I falsely accused him of not providing any. I should, instead, have explained why it doesn't count as a valid example. Just in case he actually has killfiled me, please let him know what what I just wrote. The explanation I should have provided follows: That "example" doesn't provide any basis for me to decide whether or not his claim has any merit. I could trivially write a function foo() which matches his description, and is overloaded to do different things for int16_t and int. That would be a dangerous thing to do, since int16_t could be a typedef for int. However, if that isn't the case, then foo(-42i16) would in result in a different overload being called than would be called by foo(42i16). However, as far as I can tell, the same would be true of foo(-fourtytwo_16) and foo(fortytwo_16), so I don't see how this has anything to do with user-defined literals. If Alf had bothered to provide an example that does show a difference between foo(-42i16) and foo(-fortytwo_16), then I could have compiled it, confirmed that it has the behavior he claims, and could then sit down to try to figure out how that was happening. Without an example, I am left with my assumption that he's gotten confused about something, but I'm not sure what. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 09:57PM +0200 On 16.04.2020 20:52, Keith Thompson wrote: > Alf, I took a quick look at the posts upthread from this one, > and I didn't see an example from you. It's utterly implausible > that James would lie about something like that, Not as I've known him on Usenet. It's not the first time. I don't plonk people willy-nilly or lightly; too few people left, everybody counts. > reason to. It's possible that there's an honest disagreement about > what constitutes a valid example, or that he overlooked something. > Can you repost the example you're referring to? Two posts up: > entirely wrong (unintended) thing, for example if foo copies a value > into a memory area representing a file header, and advances a write > position. When James deliberately set out to misinterpret that as nonsense, then one post up: > dependency I sketched was how many bytes it would write and advance a > write position, but there are infinitely many other reasonable such > dependencies in C++. - Alf |
Vir Campestris <vir.campestris@invalid.invalid>: Apr 16 09:59PM +0100 On 14/04/2020 13:35, Juha Nieminen wrote: > This means effectively that const functions shouldn't have > side-effects. Even if the function doesn't modify *this, it should > still have no side-effects (such as calling std::cout, etc). The problem there is if a const member function alters mutable data inside the object - say a cache... Andy |
Ike Naar <ike@faeroes.freeshell.org>: Apr 16 09:11PM > mat << 1, 2, > 3, 4; > Eigen::Matrix2d mat = mat*mat; // NOT HERE That looks like a redeclaration of 'mat'. |
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Apr 16 02:11PM -0700 Keith Thompson <Keith.S.Thompson+u@gmail.com> writes: [...] > reason to. It's possible that there's an honest disagreement about > what constitutes a valid example, or that he overlooked something. > Can you repost the example you're referring to? Alf, James has posted a followup. Since you say you've plonked him, you might not have seen it. Just FYI, I offer no advice. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips Healthcare void Void(void) { Void(); } /* The recursive call of the void */ |
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Apr 16 02:19PM -0700 > On 16.04.2020 20:52, Keith Thompson wrote: [...] >> entirely wrong (unintended) thing, for example if foo copies a value >> into a memory area representing a file header, and advances a write >> position. He has addressed that. > When James deliberately set out to misinterpret that as nonsense, Your use of the word "deliberately" suggests an insight into his thinking that I do not believe you possess. [...] -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips Healthcare void Void(void) { Void(); } /* The recursive call of the void */ |
Daniel P <danielaparker@gmail.com>: Apr 16 02:49PM -0700 On Thursday, April 16, 2020 at 5:11:18 PM UTC-4, Ike Naar wrote: > > 3, 4; > > Eigen::Matrix2d mat = mat*mat; // NOT HERE > That looks like a redeclaration of 'mat'. Ah, should be Eigen::Matrix2d mat2 = mat*mat; // NOT HERE but the point remains the same, auto can't be used here. Daniel |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 11:50PM +0200 On 16.04.2020 23:19, Keith Thompson wrote: >>> into a memory area representing a file header, and advances a write >>> position. > He has addressed that. He may have tried to the best of his ability. But being incompetent in an area of expertise, does not exonerate him from lying. > Your use of the word "deliberately" suggests an insight into his > thinking that I do not believe you possess. > [...] You're right, sorry. - Alf |
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Apr 16 04:16PM -0700 > On 16.04.2020 23:19, Keith Thompson wrote: [...] > He may have tried to the best of his ability. > But being incompetent in an area of expertise, does not exonerate him > from lying. You've chosen not to read what he has to say, which is of course your right. But accusing him of lying without paying attention to his response seems a bit much. And again, the word "lying" implies deliberate intent, which requires insight into his state of mind that I presume you do not possess. If I were in your position, I would either drop this or see what James has to say before replying. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips Healthcare void Void(void) { Void(); } /* The recursive call of the void */ |
Bonita Montero <Bonita.Montero@gmail.com>: Apr 16 08:03PM +0200 I just wrote a callback for some Windows-API with a lambda. I won- dered this works although you can't specify a calling convention for the lambda-function. I first thought that with Windows 64 bit most of the calling conventions have been leveled (__cdecl, __stdcall and __fastcall are now the same) so they are now compatible with the Windows-callbacks. But then I thought, that when I pass them as a function-pointer my compiler might generate some wrapper-code before the actual lambda-function. I tested this with a little program: #include <iostream> #include <iomanip> using namespace std; int main() { auto f = []( int a, int b, int c ) -> int { return a + b + c; }; int (__cdecl *fCdecl)( int, int, int ) = f; int (__stdcall *fStdCall)( int, int, int ) = f; int (__fastcall *fFastCall)( int, int, int ) = f; cout << hex << fCdecl << " " << fStdCall << " " << fFastCall << endl; } So compiled with MSVC / 32 bit, I get thee different pointer. With MSVC / 64 bit, the pointers are all the same. So what about g++. Are there also differnt calling conventions whch are adapted in this way ? |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 16 08:22PM +0200 On 16.04.2020 20:03, Bonita Montero wrote: > With MSVC / 64 bit, the pointers are all the same. > So what about g++. Are there also differnt calling conventions > whch are adapted in this way ? g++ needs to conform to a system's conventions in order to be usable on a system. However it can accept code that the system compiler doesn't accept, e.g. (*[]( int x ) -> int { return x; }); Disclaimer: I didn't try this now, but with Visual C++ I would expect an error because last I time I did something like that it complained about the calling convention wrappers it itself generated resulting in ambiguous overload resolution. - - - The formal is a very different kettle of fish. There the question is whether a pointer to function with C++ calling convention (the default, or `extern "C++") converts implicitly to pointer to ditto function with C calling convention, `extern "C"`. In practice it does, of course; that interoparbility and ability to leverage C libraries was a main design goal. But as far as I know the formal standard does not in any place permit that. However, also as far as I know only a Solaris compiler has ever complained by default, and then as a warning. - Alf |
Paavo Helde <eesnimi@osa.pri.ee>: Apr 16 09:34PM +0300 16.04.2020 21:03 Bonita Montero kirjutas: > With MSVC / 64 bit, the pointers are all the same. > So what about g++. Are there also differnt calling conventions > whch are adapted in this way ? I guess this depends more on the target architecture and not so much on the compiler vendor. For example, cygwin g++ 9.3.0 with x86_64-pc-cygwin target compiles your program and prints 3 identical pointers (after I added casts to void*, otherwise it printed "1 1 1"). |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Apr 15 03:47PM -0700 > https://www.infoworld.com/article/3535795/c-plus-plus-proposal-dismisses-backward-compatibility.html > "Proposal to the C++ standards committee would give up backward and > binary compatibility for safety and simplicity" Reading the actual proposal reminds me of a comment made about another programming language proposal from many many years ago: "An exercise in group wishful thinking." |
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