"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Jan 30 02:02PM +0100
On 30 Jan 2022 13:12, Ben Bacarisse wrote: > implementations not providing a useful freopen function. > But then maybe freopen simply can't be implemented in Windows for some > mysterious reason I don't get. I guess you're talking about using standard C++ code with some system specific knowledge such as how to specify the standard input stream as a file name. To reopen the standard input so that it connects to the original source, which might a pipe or a file or the console, one needs to (1) identify that source, and (2) open that source, possibly after closing the original connection. Neither is feasible in general, even (AFAIK) in Unix environment. But one might attempt to sidestep (1) by using a filename that in the relevant OS denotes standard input. A demonstration of an approach that fails is not a proof that no solution exists, e.g. failure to open a door doesn't prove that the door is stuck. Maybe the person just failed to consider using the doorknob, dragged the door instead of pushing, failed to note that it opens sideways, didn't swipe the id card, deliberately made it look as if the door didn't open, or something. But anyway I cooked up some code: #include <stdlib.h> // EXIT_... #include <stdio.h> #include <stdexcept> // std::runtime_error, std::exception #include <string> // std::string using namespace std; #ifdef PORTABLE constexpr bool portable = true; #else constexpr bool portable = false;
Juha Nieminen <nospam@thanks.invalid>: Jan 28 07:35AM
> public: > explicit B(e) {}; > }; 'explicit' is a severely underused keyword. (So much so that some C++ experts think that it should be the default, and that it would be better if you had to explicitly use an 'implicit' keyword to make the constructor implicit, rather than the other way round. But alas, backwards compatiblity...) | "Öö Tiib" <ootiib@hot.ee>: Jan 28 03:27AM -0800
On Friday, 28 January 2022 at 09:35:59 UTC+2, Juha Nieminen wrote: > if you had to explicitly use an 'implicit' keyword to make the constructor > implicit, rather than the other way round. But alas, backwards > compatiblity...) Similar situation is with overused keyword "const", it would benefit language to have it default and some kind of "var" or "mut" for mutable stuff. Alas, backwards compatibility. | "Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Jan 28 01:30PM +0100
On 28 Jan 2022 12:27, Öö Tiib wrote: > Similar situation is with overused keyword "const", it would benefit language > to have it default and some kind of "var" or "mut" for mutable stuff. > Alas, backwards compatibility. #define VAR auto #define LET const auto - Alf |
wij <wyniijj@gmail.com>: Jan 27 02:50AM -0800
On Wednesday, 26 January 2022 at 21:55:46 UTC+8, Paavo Helde wrote: > compile it and reports an ambiguity. Who is right? > PS. If one replaces foo(A&&) with foo(const A&), then also MSVC reports > an ambiguity. Another quick thought without testing: The ctor B(e) might consider using 'explicit' class B { public: explicit B(e) {}; }; |
Paavo Helde <eesnimi@osa.pri.ee>: Jan 26 03:55PM +0200
Today I was surprised by an unexpected conversion from NSVC++ 2019. The code example is here: #include <iostream> class A { public: A(size_t) {} }; enum e { e1, e2, e3 }; class B { public: B(e) {} }; void foo(A&&) { std::cout << "Overload A\n"; } void foo(const B&) { std::cout << "Overload B\n"; } int main() { foo(e1); } MSVC++ 2019 compiles this fine and chooses overload A. g++ refuses to compile it and reports an ambiguity. Who is right? PS. If one replaces foo(A&&) with foo(const A&), then also MSVC reports an ambiguity. | "Öö Tiib" <ootiib@hot.ee>: Jan 26 06:43AM -0800
On Wednesday, 26 January 2022 at 15:55:46 UTC+2, Paavo Helde wrote: > compile it and reports an ambiguity. Who is right? > PS. If one replaces foo(A&&) with foo(const A&), then also MSVC reports > an ambiguity. I suspect gcc is correct but cant bring normative proofs. The MSVC has its legacy of being overly permissive there. I'm not even sure if it has stopped accepting rvalues to initialise non-const lvalue references yet. Anyway I like whatever diagnostics of doubt about whatever implicit conversions as these keep causing major illusions and confusions. | Bonita Montero <Bonita.Montero@gmail.com>: Jan 26 03:50PM +0100
Am 26.01.2022 um 15:43 schrieb Öö Tiib: > accepting rvalues to initialise non-const lvalue references yet. Anyway I > like whatever diagnostics of doubt about whatever implicit conversions > as these keep causing major illusions and confusions. Maybe it would help to use a scoped enum which hasn't an implicit conversion to int. | Paavo Helde <eesnimi@osa.pri.ee>: Jan 26 06:26PM +0200
26.01.2022 16:50 Bonita Montero kirjutas: >>> Today I was surprised by an unexpected conversion > Maybe it would help to use a scoped enum which hasn't an implicit > conversion to int. Yes, in new code scoped enums should be certainly preferred. Changing them the scoped enums in old codebases is harder. | Bonita Montero <Bonita.Montero@gmail.com>: Jan 26 05:32PM +0100
Am 26.01.2022 um 17:26 schrieb Paavo Helde: >> conversion to int. > Yes, in new code scoped enums should be certainly preferred. Changing > them the scoped enums in old codebases is harder. When I have enums which are part of a namespace I mostly don't use them, when I have enums which are part of a class I never use them. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 26 08:46AM -0800
On 1/26/2022 5:55 AM, Paavo Helde wrote: > compile it and reports an ambiguity. Who is right? > PS. If one replaces foo(A&&) with foo(const A&), then also MSVC reports > an ambiguity. The example can be simplified by taking `enum` out of the picture #include <iostream> class A { public: A(int) {} }; class B { public: B(int) {} }; void foo(A&&) { std::cout << "Overload A\n"; } void foo(const B&) { std::cout << "Overload B\n"; } int main() { foo(0); } it will still exhibit the same behavior, but now it is fully "symmetrical" with regard to `A` and `B`. Experimenting with the example shows that MSVC seems to over-prioritize the existing rule that binding rvalue reference to an rvalue is better than binding an lvalue reference http://eel.is/c++draft/over.ics.rank#3.2.3 However, in this case the two conversion sequences are incomparable because they involve _different_ user-defined conversions. (In order to be comparable they should use the same user-defined conversion.) This is what is supposed to be recognized as an ambiguity by the compiler http://eel.is/c++draft/over.ics.rank#3.3 If we simplify the example to a mere #include <iostream> class A { public: A(int) {} }; void foo(A&&) { std::cout << "Overload rvalue\n"; } void foo(const A&) { std::cout << "Overload lvalue\n"; } int main() { foo(0); } both candidate paths will use the the same user-defined conversion. The call is no longer ambiguous, since the above "rvalue-is-better-than-lvalue" logic kicks in. -- Best regards, Andrey Tarasevich |
Bonita Montero <Bonita.Montero@gmail.com>: Jan 21 10:00AM +0100
Am 20.01.2022 um 18:35 schrieb Andrey Tarasevich: > What it basically boils down to is that C++20's `consteval` triggers a > hard error for non-compile-time evaluations, as opposed to `constexpr`, > which silently opted for run-time evaluation. "if constexpr( )" is always compile-time evaluated. | Juha Nieminen <nospam@thanks.invalid>: Jan 21 09:03AM
>> hard error for non-compile-time evaluations, as opposed to `constexpr`, >> which silently opted for run-time evaluation. > "if constexpr( )" is always compile-time evaluated. 'if constexpr' is not really a constexpr construct. It simply reuses the keyword. | Bonita Montero <Bonita.Montero@gmail.com>: Jan 21 12:51PM +0100
Am 21.01.2022 um 10:03 schrieb Juha Nieminen: >>> which silently opted for run-time evaluation. >> "if constexpr( )" is always compile-time evaluated. > 'if constexpr' is not really a constexpr construct. ... Of course, the expression has to be compile-time evaluated. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 21 10:19AM -0800
On 1/21/2022 1:00 AM, Bonita Montero wrote: >> hard error for non-compile-time evaluations, as opposed to >> `constexpr`, which silently opted for run-time evaluation. > "if constexpr( )" is always compile-time evaluated. `if constexpr` can be used to test compile-time nature of an expression, but it can only be done "from the outside" of that expression. I don't immediately see how would one go about using `if constexpr` for function argument verification, especially considering that that verification has to be injected after template argument deduction. In order to achieve that injection they used a well-known technique, which is as old as the world itself. They implemented an intermediate pass-through `Checker` class, which performs the necessary checks in its constructor. Nothing new here. However, previously we would only be able generate run-time errors (assertion failures) from inside that constructor. That's how this technique has been used for ages. They show that now, by declaring that constructor as `consteval`, one can also generate compile-time errors (!) from inside that constructor. Of course, by doing so one's restricting oneself to compile-time arguments exclusively. But apparently in their application they don't mind that. And, if I'm mot mistaken, one can get the best of both worlds by branching on `if (std::is_constant_evaluated)`/`if consteval`. -- Best regards, Andrey Tarasevich | Bonita Montero <Bonita.Montero@gmail.com>: Jan 21 07:51PM +0100
Am 21.01.2022 um 19:19 schrieb Andrey Tarasevich: > arguments exclusively. But apparently in their application they don't > mind that. And, if I'm mot mistaken, one can get the best of both worlds > by branching on `if (std::is_constant_evaluated)`/`if consteval`. You can virtually even check parameters with it: template<bool B> void f( bool_constabt<B> x ) { if constexpr( B ) ... } I use it very often for templated parameters, sometimes to check their type-properties, f.e. if they're integral. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 21 12:03PM -0800
On 1/21/2022 10:51 AM, Bonita Montero wrote: > } > I use it very often for templated parameters, sometimes > to check their type-properties, f.e. if they're integral. You are checking a template argument - an entity that is inherently compile-time. "There is no renown in that." Many techniques are available for checking template arguments at compile time even in C++98. Meanwhile, they want to check regular function arguments (!) at compile time (under assumption that they are supplied with compile-time arguments, of course). That is a wholly different story. And they also want to be able to check template arguments based on such regular function arguments. E.g. #include <string_view> #include <type_traits> template <typename T> struct Checker { consteval Checker(const char* fmt) { if (fmt == std::string_view{ "int" } && std::is_same_v<T, int>) return; if (fmt == std::string_view{ "double" } && std::is_same_v<T, double>) return; throw; } }; template <typename T> void fmt(std::type_identity_t<Checker<T>> checked, T) {} int main() { fmt("int", 42); // OK fmt("double", 1.23); // OK fmt("int", "foo"); // Compile error } -- Best regards, Andrey Tarasevich |
wij <wyniijj@gmail.com>: Jan 20 02:37AM -0800
On Wednesday, 19 January 2022 at 09:24:37 UTC+8, Lynn McGuire wrote: > effectively eliminate an entire class of runtime bugs by hoisting a > check into compile-time. Let's get right into it!" > Lynn The idea of using errno for error-handling (mandatory) had developed nearly 20 years ago and very successful. https://sourceforge.net/projects/cscall/files/latest/download It is about error checking, or inline test (programmers don't like this term). Coding syntax in this preliminary level may be too early (C++ is already burdensome , mostly developed from enthusiasm, ideal and insufficient experiences. The result might just be adding burdens, not 'feature', years latter). Hope the language developer may realize the issue of error-handling is the foundation of robust and efficient programs rather than syntax sugars. (Exception is not suitable for error-handling) | Juha Nieminen <nospam@thanks.invalid>: Jan 20 11:43AM
> The idea of using errno for error-handling (mandatory) had developed > nearly 20 years ago and very successful. 'errno' alone may not be enough for error handling because sometimes you want or need more details about the error. Also, while not a show-stopper, 'errno' is easy to use wrong. Consider how easy it is to do something like this: if(!someFunction()) std::cerr << "Error calling someFunction(): " << std::strerror(errno); What's wrong with that? The fact that it's at least theoretically possible that that first operator<<() may change the value of errno (if it itself encounters some kind of error), in which case it's going to print the wrong error message. (This might be the reason for those infamous and hilarious "Error: No error" messages seen sometimes. It's not that there was no error, but that 'errno' (or whatever the program uses) was changed in between the actual error and showing the message.) (Sure, the proper way to handle this is to take the value of errno into a variable immediately after the function call, but consider how easy it's to do mistakes like the one above.) | Muttley@dastardlyhq.com: Jan 20 04:14PM
On Thu, 20 Jan 2022 11:43:11 -0000 (UTC) >"Error: No error" messages seen sometimes. It's not that there was no >error, but that 'errno' (or whatever the program uses) was changed >in between the actual error and showing the message.) AFAIK the standard C library only sets errno if an error occurs. It never resets it to zero if there was no error. One would assume the C++ standard libraries behave the same way so "Error: no error" would only occur if the program has mistakenly seen a return value as a system error which would set errno when it might actually be a higher level error which doesn't. | James Kuyper <jameskuyper@alumni.caltech.edu>: Jan 20 11:57AM -0500
On 1/20/22 6:43 AM, Juha Nieminen wrote: ... > "Error: No error" messages seen sometimes. It's not that there was no > error, but that 'errno' (or whatever the program uses) was changed > in between the actual error and showing the message.) When I've been in a position to check such things, I've usually found that they are the result of calling perror() without first checking whether errno was non-zero. When you use perror(), you should check to make sure under which circumstances the previously called function sets errno. Some things that are considered errors from the point of view of the developer do not actually result in errno being set. | wij <wyniijj@gmail.com>: Jan 20 09:06AM -0800
On Thursday, 20 January 2022 at 19:43:27 UTC+8, Juha Nieminen wrote: > (Sure, the proper way to handle this is to take the value of errno into > a variable immediately after the function call, but consider how easy > it's to do mistakes like the one above.) What programs need is a way to branch, errno is one basic way to do this. I use class Errno that contains only one int data member. class Errno { int m_errno; public: constexpr Errno(); constexpr Errno(const Errno&); ... }; My experience of using class Errno says errno (thread-local variable) could be an option if such impl. could be faster(and smaller). I did not recommend my implement but two points (debating such an issue might be unnecessarily long and fruitless). 1. Make error checking mandatory 2. Use __FILE__, __LINE__ to mark WHERE error detected. As I know, no programming language treats error-testing an essential part. Most programmers don't like to use and see error-testing(or handling) codes for each function call. But I found no way to escape, and my experience showed that explicit testing for errors and handling it in the end is 'efficient', at least for programmer's time (and thus money as well). Others are implementation problems. | Ben Bacarisse <ben.usenet@bsb.me.uk>: Jan 20 05:14PM
> As I know, no programming language treats error-testing an essential > part. You might want to take a look at Icon. It makes extensive (and very effective) use of the success and failure of even the most basic operations forstructuring the code. -- Ben. | James Kuyper <jameskuyper@alumni.caltech.edu>: Jan 20 12:22PM -0500
> On Thu, 20 Jan 2022 11:43:11 -0000 (UTC) > Juha Nieminen <nospam@thanks.invalid> wrote: ... >> error, but that 'errno' (or whatever the program uses) was changed >> in between the actual error and showing the message.) > AFAIK the standard C library only sets errno if an error occurs. Oddly enough, that's not the case: "The value of errno in the initial thread is zero at program startup (the initial value of errno in other threads is an indeterminate value), but is never set to zero by any library function. 218) The value of errno may be set to nonzero by a library function call whether or not there is an error, provided the use of errno is not documented in the description of the function in this document." (C standard 7.5p3). Implicit in that statement is the fact that "provided the use of errno is ... documented in the description of the function in this document.", then that function may only use errno as specified by that documentation. > ... It never > resets it to zero if there was no error. As specified above, it never sets it to zero at all; that occurs only at program startup, and only for the initial thread. > libraries behave the same way so "Error: no error" would only occur if the > program has mistakenly seen a return value as a system error which would > set errno when it might actually be a higher level error which doesn't. The C++ standard prohibits the routines specified in <system_error>, from changing the value of errno (19.5p2). Otherwise, it never says anything to either mandate or prohibit changing errno. I would hope that what the C standard says about the use of errno by the C standard library can, by extension, be applied to the rest of the C++ standard library, but there's nothing that says so explicitly. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 20 09:35AM -0800
On 1/18/2022 5:24 PM, Lynn McGuire wrote: > post is not about how you can use it, but rather how we used it to > effectively eliminate an entire class of runtime bugs by hoisting a > check into compile-time. Let's get right into it!" What it basically boils down to is that C++20's `consteval` triggers a hard error for non-compile-time evaluations, as opposed to `constexpr`, which silently opted for run-time evaluation. -- Best regards, Andrey Tarasevich | Ian Collins <ian-news@hotmail.com>: Jan 21 08:50AM +1300
On 20/01/2022 23:37, wij wrote: > Hope the language developer may realize the issue of error-handling is the > foundation of robust and efficient programs rather than syntax sugars. > (Exception is not suitable for error-handling) So it's better to pick up the error at compile time, isn't it? The gcc and clang compilers have something similar for printf like logging with __attribute__ ((format (printf, FORMAT_ARG, VARG_BEGIN))) which has most likely saved many a run time error in similar situations. -- Ian. | legalize+jeeves@mail.xmission.com (Richard): Jan 20 10:19PM
[Please do not mail me a copy of your followup] Lynn McGuire <lynnmcguire5@gmail.com> spake the secret code >"How we used C++20 to eliminate an entire class of runtime bugs" by >Cameron DaCamara >https://devblogs.microsoft.com/cppblog/how-we-used-cpp20-to-eliminate-an-entire-class-of-runtime-bugs/ Nice success story! -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Terminals Wiki <http://terminals-wiki.org> The Computer Graphics Museum <http://computergraphicsmuseum.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
James Kuyper <jameskuyper@alumni.caltech.edu>: Jan 18 07:03PM -0500
On 1/18/22 2:32 PM, Andrey Tarasevich wrote: ... > this?" "Why is it legal?" To me the answer is natural and obvious: why > not? No harm done, no need to overcomplicate things. This has always > been one of the cornerstone principles in thois language's design. When you're claiming that what it actually says in 9.2.1p2 is irrelevant, and stuff that it says nowhere in 9.2.7 is actually the primary purpose of the 'inline' specifier, then you're going to need better arguments than those. Having confirmed that you don't have any, I'm going to leave this discussion - it's not going anywhere. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 18 05:22PM -0800
On 1/18/2022 4:03 PM, James Kuyper wrote: > primary purpose of the 'inline' specifier, then you're going to need > better arguments than those. Having confirmed that you don't have any, > I'm going to leave this discussion - it's not going anywhere. Sigh... It appears that I'm trying to prove that Earth is round at a meeting of Flat Earth Society. "You're going to need better arguments than those", they say... ))) Sorry, boys, but to prevent any further time-wasting I'm going to have to pull the rank here: at this point just study, take notes and commit to memory what I said above. If you feel that you need extra rationale/argumentation to better understand the material - you'll find all that later (leave to you as an exercise). -- Best regards, Andrey Tarasevich |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Jan 17 03:29PM -0800
> inline` is always redundant. It is 100% equivalent to plain > `static`. There's never any tangible reason to use `static` and > `inline` together. [...] In C++ I expect that's right. In C though there is a key difference that may provide a reason to use 'inline'. In C declaring or defining a function 'inline' can provide additional guarantees beyond just using 'static'. In the semantics section of 6.7.4 of the C standard, there is this excerpt: Making a function an inline function suggests that calls to the function be as fast as possible. The extent to which such suggestions are effective is implementation-defined. Note the second sentence. C implementations must document what happens with 'inline', but there is no such requirement for 'static'. (I should add that I'm assuming that C++ does not impose a similar requirement. I have not tried looking in the C++ standard to see if that is the case.) | Tim Rentsch <tr.17687@z991.linuxsc.com>: Jan 17 03:34PM -0800
> should be performed. In C, it's an ignorable hint that "that calls to > the function be as fast as possible.", with the method where by that > might be achieved being unspecified. Note the sentence in the semantics portion of 6.7.4 of the C standard that says The extent to which such suggestions are effective is implementation-defined. So I think "implementation-defined" is more accurate than "unspecified". | "james...@alumni.caltech.edu" <jameskuyper@alumni.caltech.edu>: Jan 17 03:50PM -0800
On Monday, January 17, 2022 at 6:35:03 PM UTC-5, Tim Rentsch wrote: > James Kuyper <james...@alumni.caltech.edu> writes: ... > implementation-defined. > So I think "implementation-defined" is more accurate than > "unspecified". "implementation-defined" behavior is unspecified behavior that an implementation is required to document, so both terms are correct, but "implementation-defined" is more specific. However, the point I was making was about what the standard failed to specify, so "unspecified" was relevant - that an implementation is required to document the behavior wasn't. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 17 04:28PM -0800
On 1/17/2022 1:15 PM, James Kuyper wrote: > You might disapprove of such a feature, but being a non-mandatory hint > is the primary purpose of this feature. It seems odd to me to mention > the primary purpose of a feature only in a footnote. That is false. The primary purpose of this feature is, again, is consistent between functions and variables: facilitate support for multiple definitions of the the same entity with external linkage (variable or function) across multiple TUs (see below) > Could you show me how you would use inline for the purpose of violating > the ODR rules, where there's no more appropriate way to achieve the same > objective? Allowing you do do so was certainly not the purpose of 'inline'. Um... This is some rather strange wording: "use inline for the purpose of violating the ODR rules". Where did you get this? ODR rules, obviously, are aware of `inline` and inclusive of `inline`. Nobody's talking about "violating" them here in any formal way. What I'm referring to is rather obvious from the above discussion. The primary purpose of the feature is this: the whole problem, the whole objective is provide us with a feature, that would let us write unsigned foo = 42; void bar() { } in a header file and then include this file into multiple TUs. A naive attempt to do this will result in ODR violations: multiple definitions of entities with external linkage. (`static` might be seen as workaround for the function, but we don't want that. External linkage is the point here. For whatever reason, we want a function with unique address identity for the whole program.) So, we need a feature that will 1) suppress the ODR violations, and 2) ensure the unique address/storage identity for both the variable and the function. And (drum roll) that is achieved through `inline` inline unsigned foo = 42; inline void bar() { } Done. End of story. And no point any "hints" or "embedding of function call sites" come into this picture. They are completely irrelevant. As a side note, of course, the reason we want this is to expose full function body to the compiler in all TUs, thus helping the compiler to fully analyze the function, optimize its usage and optimize surrounding code based on its knowledge of function's internal behavior. The actual "embedding of function call sites" is just one [minor] part of this process. How the compiler will implement the required spec is its own business, but as we all know the most popular approach today is to just push the problem over to the linker and let it silently eliminate the unnecessary copies. This implementational approach is what justifies referring to `inline` as an "ODR defeater" informally. That's basically what `inline` does: it tells the linker that instead of complaining about multiple definitions it has to shut up and just mop things up quietly. That is the prime purpose of `inline`. Has always been. All these stories about the "hint" is just a mumbo-jumbo, which probably only persists in standard text out of respect to someone who originally introduced it. -- Best regards, Andrey Tarasevich | James Kuyper <jameskuyper@alumni.caltech.edu>: Jan 17 10:55PM -0500
On 1/17/22 7:28 PM, Andrey Tarasevich wrote: ... > of violating the ODR rules". Where did you get this? ODR rules, > obviously, are aware of `inline` and inclusive of `inline`. Nobody's > talking about "violating" them here in any formal way. You're talking about 'inline' as a way of getting around those rules. If 'inline' didn't exist, then those rules obviously couldn't cite it as an exception, and what you're claiming is the primary purpose of 'inline' would be a violation of those rules. That purpose is expressed by inserting an exception for 'inline' into those rules. > { > } > in a header file and then include this file into multiple TUs. Declaring them with internal linkage would achieve the same benefit. ... > (`static` might be seen as workaround for the function, but we don't > want that. External linkage is the point here. For whatever reason, we > want a function with unique address identity for the whole program.) Why? I can imagine unusual circumstances where that might be needed, but I wouldn't expect it to be a common need. Note that an inline function is only required to have a unique address if it has external linkage or module linkage (9.2.7p6). If having a single unique address was the main purpose of 'inline', why would it even be permitted to declare an inline function with internal linkage? You could have functions with internal linkage if you don't need a unique address, and 'inline' functions, with inherently external linkage, if you do need a unique address. If that's the primary purpose, why didn't they it that way? ... > stories about the "hint" is just a mumbo-jumbo, which probably only > persists in standard text out of respect to someone who originally > introduced it. That's a ridiculous suggestion. That's not how standards get written. If getting around the ODR rules was even an important secondary purpose for 'inline', there would have been some mention of it somewhere in 9.2.7. That ridiculous suggestion isn't even consistent with the immediately preceding sentence. If the person who originally introduced it had a different conception of the purpose, one that the standard pays only lip service to, then by definition the purpose you refer to has not "always been" the prime purpose. There had to have been at least a short period of time (as I understand it, that "short" period of time has been decades long) during which the purpose it originally was introduced for remained the primary purpose. | Bonita Montero <Bonita.Montero@gmail.com>: Jan 18 09:20AM +0100
Am 17.01.2022 um 19:13 schrieb Andrey Tarasevich: > of the keyword. > Currently, this is essentially a defect in the standard, which only > serves to confuse people. You're simply an idiot who can't accept the world like it is. | Andrey Tarasevich <andreytarasevich@hotmail.com>: Jan 18 11:32AM -0800
On 1/17/2022 7:55 PM, James Kuyper wrote: > exception, and what you're claiming is the primary purpose of 'inline' > would be a violation of those rules. That purpose is expressed by > inserting an exception for 'inline' into those rules. When I'm talking about `inline` as an "ODR suppressor" or "defeater", I'm not talking about the exact formal ODR as it is defined in C++ standard. I'm referring to the general/basic/primitive linker-level idea of ODR: "if you have two identical symbols in you object files, you end up with multiple definition error from the linker". >> } >> in a header file and then include this file into multiple TUs. > Declaring them with internal linkage would achieve the same benefit. No it won't. In that case they won't have external linkage, meaning that they will not refer to the same entity everywhere in the program. I think I made it clear that this is part of the objective as well. >> want a function with unique address identity for the whole program.) > Why? I can imagine unusual circumstances where that might be needed, but > I wouldn't expect it to be a common need. For variables the common need is obvious. (I'm surprised I have to even mention it.) At the same time, I agree that for functions it is much less important and common. But if this were just about inline functions, chances are nobody would even bother. However, in C++ the primary driver for this functionality is not really inline functions at all. It is templates. 99.9% of the pressing need for this "ODR suppressing" functionality comes from templates. Implicit instantiation of templates faces the very same issues: external linkage and multiple definitions, conflicting with the "basic" idea of ODR I described above. Templates are the primary driver behind that "gratuitously-overgenerate-then-discard" approach relied upon by modern implementations. (There were alternative implementations, intended to avoid "overgeneration" and to play nice with "basic ODR", e.g. Sun Solaris C++ compiler, but AFAIK none of them survived.) (C++ also provides you with facilities for explicit "manual" instantiation of templates, which are, curiously, quite similar to C's approach to `inline`. But I hope it is clear to everyone that C++ templates would never take off without their _implicit_ instantiation mechanics.) So, whether you consider inline functions with external linkage necessary or not is not really that important. The "gratuitously-overgenerate-then-discard" approach would still be there anyway - for templates. And once it's there, you basically get inline functions with external linkage for free, just for the sake of formal completeness. > module linkage (9.2.7p6). If having a single unique address was the main > purpose of 'inline', why would it even be permitted to declare an inline > function with internal linkage? "Having a single unique out-of-line body" is probably better wording. Having a single unique address naturally comes with it. As for why inline functions with internal linkage are permitted... There's simply no reason to prohibit it. `static inline` is redundant, but there's no reason to spend effort to introduce a rule that would prohibit it. There lots and lots of such redundant yet legal constructs in C++. > linkage if you don't need a unique address, and 'inline' functions, with > inherently external linkage, if you do need a unique address. If that's > the primary purpose, why didn't they it that way? Again, that would lead to a more complicated specification of this specifier without any tangible benefits. No need to do that. Here's an immediately available example for you: variables again. Note that `static inline` is _obviously_ redundant with namespace-scope variables. There's no need to involve any "woulds", "whatifs" or contrived scenarios here. `static inline` is obviously and unconditionally redundant with variables today, right now, everywhere. Yet, this is perfectly legal /* Namespace scope */ static inline unsigned n = 42; You can start asking your "why?" questions now. "Why do they allow this?" "Why is it legal?" To me the answer is natural and obvious: why not? No harm done, no need to overcomplicate things. This has always been one of the cornerstone principles in thois language's design. -- Best regards, Andrey Tarasevich | Tim Rentsch <tr.17687@z991.linuxsc.com>: Jan 17 04:16PM -0800
>> return r; >> } > That's very ugly. Beauty is in the eye of the beholder. Personally I think it's rather pretty. >> written using the standard C preprocessor, and left as an >> exercise for any ambitious readers.) > Exercise for you: test that unrevealed macro with Visual C++. I don't have access to a Visual C++ system. The macro I wrote compiles under gcc and g++ as C99, C11, C++11, C++14, and C++17, using a -pedantic-errors option. (Using clang and clang++ gives the same results.) > (Making it work also with Visual C++ is doable.) Does this need more than just writing the definition in conforming C11 or C++11? >> find, maybe someone else can do better. > The `for(;;)`, `continue;` and `break;` constructs come to mind, so > you're right, someone else can do better. It is of course possible to write the function without using any 'goto's. Whether such a writing is an improvement is another matter. To my eye the goto version expresses the structure more directly and in a form that is easier to comprehend. I did try actually writing a version with a continue-able loop around the switch(), but it looked clumsy and not as easy to follow as the goto version. If you have an example to post I definitely would like to see it. > At a guess the code counts the number of Unicode code points? Yes, sorry, I thought that was apparent from the earlier context. > Does it return -1 also for "too long" UTF-8 sequence? The -1 return values is for character sequences that are not syntactically well-formed for UTF-8. > Smart compiler removed all the `if`'s? > I guess you're flame-baiting a little just for fun, but I chose to > take it seriously. I'm not flame-baiting at all. By "main code path" I mean that part of the code that deals with normal ASCII characters (and not counting the terminating null character). The switch() statement generates an unconditional indirect branch, indexing a table of 32 targets, and the 15 targets for normal ASCII characters simply go around the loop again, with no tests. I expect you can see that based on the line with 15 cases, which just does a 'goto' to start the loop again. | Tim Rentsch <tr.17687@z991.linuxsc.com>: Jan 17 10:43PM -0800
>> written using the standard C preprocessor, and left as an >> exercise for any ambitious readers.) > Exercise for you: test that unrevealed macro with Visual C++. Update after my previous message. The cases() macro I wrote has now been tested with Visual C++, and it compiles there without complaint. | Bonita Montero <Bonita.Montero@gmail.com>: Jan 18 09:18AM +0100
Am 17.01.2022 um 19:28 schrieb Tim Rentsch: > } > return r; > } That's not C or C++ and it does have a lot of branch-mispredictions. | "Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Jan 18 12:09PM +0100
On 18 Jan 2022 07:43, Tim Rentsch wrote: > Update after my previous message. The cases() macro I wrote has > now been tested with Visual C++, and it compiles there without > complaint. Good to hear. Visual C++ has or had somewhat non-standard treatment of token pasting and variadic macros, and either it's been fixed, or your code avoided running into it. A comment in some old code of mine says that ❞ [Visual C++ 2017] is unable to count `__VA_ARGS__` as *n* arguments, and instead counts it as 1. Mostly. Counting the number of arguments of a variadic macro was AFAIK invented by Laurent Deniau in 2006, and it can go like this: #define N_ARGUMENTS( ... ) \ INVOKE_MACRO( \ ARGUMENT_64, \ ( \ __VA_ARGS__, \ 63, 62, 61, 60, \ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 \ ) \ ) #define ARGUMENT_64( \ a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, \ a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, \ a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, \ a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, \ a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, \ a61, a62, a63, a64, ... ) \ a64 ... where `INVOKE_MACRO` tackles the Visual C++ quirks: /// \brief Invokes the specified macro with the specified arguments list. /// \hideinitializer /// \param m The name of a macro to invoke. /// \param arglist A parenthesized list of arguments. Can be empty. /// /// The only difference between `INVOKE_MACRO` and `INVOKE_MACRO_B` is that they're /// *different* macros. One may have to use both in order to guarantee macro expansion in /// certain (not very well defined) situations. #define INVOKE_MACRO( m, arglist ) \ m arglist #define INVOKE_MACRO_B( m, arglist ) \ m arglist I've removed macro name prefixes in the above. For reusable code there better be name prefixes, to reduce or avoid name collisions. - Alf | Tim Rentsch <tr.17687@z991.linuxsc.com>: Jan 17 03:48PM -0800
> I was thinking of the Smalltalk workstations from the 80s like those > made by Tektronix. I don't think they used a VM, but ran on the bare > metal. As I recall Tektronix was one of four companies licensed by Xerox to port a Smalltalk-80 VM to other systems. One motivation for offering these licenses was to help debug the writing in "Smalltalk-80: The language and its implementation", where most of the implementation part was about how the VM works and how to write one. Given that, it would be strange if the Tektronix effort did not use a VM but instead ran a standard Smalltalk image on bare hardware. | legalize+jeeves@mail.xmission.com (Richard): Jan 18 06:47AM
[Please do not mail me a copy of your followup] Tim Rentsch <tr.17687@z991.linuxsc.com> spake the secret code >write one. Given that, it would be strange if the Tektronix >effort did not use a VM but instead ran a standard Smalltalk >image on bare hardware. Interesting! I didn't know that! I found a PDF of the book on archive.org, so I'm going to take a look at that! (If others are interested: <https://archive.org/details/smalltalk80langu00gold>) -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Terminals Wiki <http://terminals-wiki.org> The Computer Graphics Museum <http://computergraphicsmuseum.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
|