- ???Microsoft Azure CTO Mark Russinovich: C/C++ should be deprecated??? - 5 Updates
- How to initialize a complete member array only in a constexpr context ? - 3 Updates
- "Richard Stallman Announces C Reference" - 1 Update
- string_view assignment from temporary ? - 4 Updates
- Never use strncpy! - 12 Updates
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 06:16AM > deprecated,??? he said on Twitter, expressing a personal opinion rather > than a fresh Microsoft policy.??? > Wow ! Bold. Is a "Rust religion" forming in the industry? I remember when Java was supposed to be the Holy Grail and answer to everything. (Nowadays I get the feeling that Java survives solely because it's the main/only programming language available for developing for Android devices. Haven't exactly researched if it's the only possible, or just the main one.) |
| David Brown <david.brown@hesbynett.no>: Sep 22 09:45AM +0200 On 22/09/2022 08:16, Juha Nieminen wrote: >> than a fresh Microsoft policy.??? >> Wow ! Bold. > Is a "Rust religion" forming in the industry? Rust is, IMHO, far too immature to be the right choice for major projects. It has some good points, but it needs to stabilise as a language and get more and better tools before it can be an alternative to C or C++ for a lot of work. An alternative future solution is for the C++ folks to copy the useful parts from Rust. Rust's memory safety (AFAIUI) relies partly on the use of static analysis tools run on the code. There are a variety of static and dynamic analysis tools available for C++, but they are often tightly to specific compilers or expensive third-party tools. If a common standard could be formed and integrated with the standard library and with open source tools, then C++ would be as "safe" as Rust for those that chose to use these tools. > main/only programming language available for developing for Android > devices. Haven't exactly researched if it's the only possible, or just > the main one.) The preferred language for Android is now Kotlin (which runs on the same JVM virtual machine). |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 09:41AM > security and reliability, the industry should declare those languages as > deprecated,??? he said on Twitter, expressing a personal opinion rather > than a fresh Microsoft policy.??? I took a cursory look at Rust, and it really seems to embrace the "brevity-over-clarity" style of programming. What I call "brevity-over-clarity style of programming" is something I have opposed and learned to hate over the years. For some reason it appears to be what a good majority of beginner programmers naturally gravitate towards, and some of them never unlearn. It's the general use of names that are as short as possible, usually with complete disregard to readability and understandability. (For example, using the variable name "err" instead of "errorCode". Or "ret" instead of "returnValue". Or "i" instead of "index". Sometimes the word may be spelled-out rather than contracted, but in itself is very non-descript without any further qualifiers. For example a function named "convert()". Convert what? And into what?) It appears that functions in Rust are declared with a keyword. What is this keyword? Perhaps "function"? Of course not. It's "fn". Because why wouldn't it be? (It couldn't get much shorter than that, unless you want it to be just "f". Which actually wouldn't even surprise me.) In order to declare a varible as mutable you have to specify that with a keyword. And what is this keyword? Perhaps "mutable" (like in C++)? Of course not. It's "mut", because why wouldn't it be? Who cares about readability? You are saving typing a whopping 4 characters! Obviously type names are as short as possible, such as "i32" and "u64". Rust seems to also support a "using" keyword similar to the one used in C++ and some other languages. Except that, obviously, "using" is too long, so it's "use". (At least it is an entire English word.) The standard library (or at least the little I skimmed over) seems to be a bit of a mixed bag. Sometimes words might be spelled out in their entirety, but not even nearly always. (For example, what possible reason is there to call a function "gen_range" instead of "generate_range"? Is that honestly too much to type?) At least their string type is named "String". I wouldn't have been surprised if it had been "Str". Must have been a lapsus. |
| Jens Stuckelberger <Jens_Stuckelberger@nowhere.net>: Sep 22 02:01PM On Wed, 21 Sep 2022 14:04:43 -0500, Lynn McGuire wrote: > security and reliability, the industry should declare those languages as > deprecated," he said on Twitter, expressing a personal opinion rather > than a fresh Microsoft policy." A personal opinion is like an asshole: everybody's got one. |
| Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 22 04:46PM +0100 > word may be spelled-out rather than contracted, but in itself is > very non-descript without any further qualifiers. For example a > function named "convert()". Convert what? And into what?) I've seen all of these in C, so why single out Rust? Does the language itself somehow promote this style? > this keyword? Perhaps "function"? Of course not. It's "fn". Because > why wouldn't it be? (It couldn't get much shorter than that, unless > you want it to be just "f". Which actually wouldn't even surprise me.) Of course it could get shorter. In C it's nothing at all and you don't complain about that degree of brevity! > a keyword. And what is this keyword? Perhaps "mutable" (like in C++)? > Of course not. It's "mut", because why wouldn't it be? Who cares about > readability? You are saving typing a whopping 4 characters! And C has int, struct, enum, char, const, extern... I think there is a bit of a double standard here. -- Ben. |
| Timothy Madden <terminatorul@gmail.com>: Sep 22 10:27AM Hello I am writing a class template for fixed capacity strings: basic_static_string<SIZE, char, char_traits<char>> (similar to the one in boost, but my company doesn't use newest version of boost yet). I use C++14 at work, and the only data members of my class template are: std::size_t len; char buffer[SIZE]; When I instantiate and construct a static string, like: static_string<256u>("Delta") I would like to only initialize the needed characters (characters 0 to 5) and leave the other 251 characters uninitialized. I would like my class to be constexpr, since there is no memory allocation. However, somehow a constexpr constructor must use the member- initializers to initialize the entire object. Is there any way to use std::is_constant_evaluated() to completely initialize the member array in a constexpr context, and partially initialized it in a run-time context ? This would be easy to do inside a function body with an if statement, but in a member-initializer for a constructor it is not. I tried to use is_constant_evaluated() in a template argument, but then it will always return true. Why is the standard defined like that ? It makes is_constant_evaluated() useless, when you actually need it... -- Thank you, Timothy Madden |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 11:14AM > I would like my class to be constexpr, since there is no memory > allocation. However, somehow a constexpr constructor must use the member- > initializers to initialize the entire object. You mean that you wouldn't want to have the initializer that I have marked below with "// THIS"? //--------------------------------------------------------------------- #include <cstddef> template<std::size_t kCapacity> class StaticString { char mData[kCapacity]; std::size_t mLength; public: template<std::size_t kStrLength> constexpr StaticString(const char(&initStr)[kStrLength]): mData{}, // THIS mLength(kStrLength - 1) { for(std::size_t i = 0; i < kStrLength; ++i) mData[i] = initStr[i]; } constexpr const char* c_str() const { return mData; } }; #include <iostream> int main() { constexpr StaticString<256> str("hello world"); std::cout << "\"" << str.c_str() << "\"\n"; } //--------------------------------------------------------------------- |
| Timothy Madden <terminatorul@gmail.com>: Sep 22 02:32PM On Thu, 22 Sep 2022 11:14:21 -0000 (UTC), Juha Nieminen wrote: > Timothy Madden <terminatorul@gmail.com> wrote: [...] > } > constexpr const char* c_str() const { return mData; } > }; [...] The member-initializer : mData { } zeroes-out 256 characters, when I only need to initialize 12 (for "hello world"). This is the right thing to do in a constexpr context, but in a run-time context I want to avoid it. So my thought was to use std::is_constant_evaluated() somehow and only apply the initializer if the function returns true. But the syntax of a member-initializer doesn't seem to allow it. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 12:41PM > the promise that, by virtue of Rust's linear/affine type system, a > component that compiles is guaranteed to be memory correct as long as > the unsafe subset of Rust hasn't been used is quite a strong statement. I happened to stumble across this, which seems to contradict that notion: https://doc.rust-lang.org/book/ch15-06-reference-cycles.html |
| Paavo Helde <eesnimi@osa.pri.ee>: Sep 22 02:09PM +0300 I just realized it is legal to assign a temporary std::string to a std::string_view std::string foo(); void bar(std::string_view v) { if (v.empty()) { v = foo(); // BUG! } } That seems way too dangerous to me. Is there something I could do to get such things failing to compile? Would deriving from std::string_view and declaring operator=(std::string&&) as deleted be a solution? |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 11:17AM > I just realized it is legal to assign a temporary std::string to a > std::string_view I think there may be completely legitimate situations where you may want to initialize a string_view with a temporary. Like: foo(std::string_view(bar())); |
| Paavo Helde <eesnimi@osa.pri.ee>: Sep 22 03:19PM +0300 22.09.2022 14:17 Juha Nieminen kirjutas: > I think there may be completely legitimate situations where you may want > to initialize a string_view with a temporary. Like: > foo(std::string_view(bar())); Yes, I know. That's why my question was about assignment, not initialization. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 12:36PM >> foo(std::string_view(bar())); > Yes, I know. That's why my question was about assignment, not > initialization. std::string_view view; foo(view = bar()); Isn't that completely normal code? :P |
| Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 22 12:39AM +0100 >> source. > Yes, it is to copy a "C String" (Null Terminated) into a fixed width > field. Again, a quibble: not quite. A null will be respected, but there is no need for the source to be a C string. -- Ben. |
| Richard Damon <Richard@Damon-Family.org>: Sep 21 08:45PM -0400 On 9/21/22 7:39 PM, Ben Bacarisse wrote: >> field. > Again, a quibble: not quite. A null will be respected, but there is no > need for the source to be a C string. It may be able to do more, but I suspect the purpose it was designed was for that. |
| Keith Thompson <Keith.S.Thompson+u@gmail.com>: Sep 21 05:55PM -0700 >> field. > Again, a quibble: not quite. A null will be respected, but there is no > need for the source to be a C string. You're right (and I misstated it in a recent post). The standard (I'll quote N1570 because I have it open) says: The strncpy function copies not more than n characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined. Which means that the source doesn't have to point to a C string. The Linux Programmer's Manual man page incorrectly suggests that the source has to be a pointer to a C string: The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest. [...] The strncpy() function is similar, except that at most n bytes of src are copied. The phrase "the string pointed to by src" in the description of strcpy implies that the behavior is undefined if src doesn't point to a string. The man page incorrectly implies that same wording applies to strncpy. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips void Void(void) { Void(); } /* The recursive call of the void */ |
| Keith Thompson <Keith.S.Thompson+u@gmail.com>: Sep 21 06:00PM -0700 >> no need for the source to be a C string. > It may be able to do more, but I suspect the purpose it was designed > was for that. According to the standard's description, the source clearly does not have to point to a C string -- and strncpy() would work perfectly well to copy one fixed-sized buffer to another, which is a very plausible use case if you're working with such a data structure. char source[5] = "hello"; // no null terminator char target[5]; strncpy(target, source, sizeof target); Of course it also works if the source is a pointer to a C string, and it's explicitly required to deal with the null terminator. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips void Void(void) { Void(); } /* The recursive call of the void */ |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 22 04:40AM +0200 Am 21.09.2022 um 23:14 schrieb David Brown: >> Before C++11 volatile were partitially used where today you use atomics. > If you used "volatile" thinking you got the effects of atomic access, > you were wrong. ... No, you could use volatile in an implementation-defined way and it did partitially the same like atomic. >>> be a lot more expensive than using volatile writes. >> volatile writes can't be substituted with fences. > A fence implies a memory barrier - ... It's a different word for the same thing. |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 05:59AM > If you use "volatile" when you mean "atomic", your code is wrong. Yeah. Some programmers might have mistakenly thought that 'volatile' means the same thing as "atomic", and thus "thread-safe". However, programmers who know what they are doing also know that it isn't anything like that. I think that the exact semantics of 'volatile' might be largely implementation-defined, but AFAIK in most if not all compilers (especially gcc and clang) it effectively acts as an optimization barrier. It tells the compiler "any access to this must never be optimized away, nor moved to happen somewhere else in the code". In other words, if you eg. write a loop where you read a 'volatile' ten times, then the compiler must generate code that reads it ten times, inside that exact loop and nowhere else. The compiler must not optimize it to one single read or completely away (because it doesn't see anything changing it). By far the most common (perhaps only) use of this is in embedded programming, especially on very small processors, where things like ports and CPU pins are mapped into RAM (which means that reading the same memory location may give different values at different times, and writing values there most definitely must never be optimized away). There's another useful use of 'volatile': In benchmarking. Run the code that's to be benchmarked, and then assign the return value to a volatile. This stops the compiler from optimizing the whole thing away because it sees that the result isn't being used for anything. (It might not have optimized it away in the first place, but assigning the result to a volatile makes sure of that.) |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 06:03AM > I've always used memcpy. I've very seldom used any str* function > other than strlen and once in a blue moon, strtok. I use snprintf > in place of strcat, for instance. memcpy requires you to know the length of the string in advance, which is often not necessary, and would require traversing the string twice in order to copy it. (Why traverse it twice? You can copy it while traversing it for the first time!) |
| Juha Nieminen <nospam@thanks.invalid>: Sep 22 06:09AM >> i.e. the destination is considered to be a fixed-width field but not the >> source. > Yes, it is to copy a "C String" (Null Terminated) into a fixed width field. Then it should have been named something entirely different. As it is now it gets extremely easily confused with strcpy(), as if it were a "safer" variant of it, in the same was as strncat() is a "safer" variant of strcat(). |
| David Brown <david.brown@hesbynett.no>: Sep 22 08:56AM +0200 On 22/09/2022 04:40, Bonita Montero wrote: >> you were wrong. ... > No, you could use volatile in an implementation-defined way and it did > partitially the same like atomic. What do you think "volatile" does, as a qualifier for accesses? What do you think "atomic" means - both in terms of what the C and C++ standards say, and what the term means more generally in programming? There is a degree of overlap, but they are not the same thing. >>> volatile writes can't be substituted with fences. >> A fence implies a memory barrier - ... > It's a different word for the same thing. No, they are different things. Atomic fences are about synchronisation between different threads - they ensure that threads running on different cores see a consistent picture of the relevant data in memory. Memory barriers are about ordering of the /local/ view of memory reads and writes. |
| David Brown <david.brown@hesbynett.no>: Sep 22 09:32AM +0200 On 22/09/2022 07:59, Juha Nieminen wrote: > (especially gcc and clang) it effectively acts as an optimization > barrier. It tells the compiler "any access to this must never be > optimized away, nor moved to happen somewhere else in the code". Yes. You can think of "volatile" as telling the compiler "there are things you don't know about that mean you can't apply as-if optimisations here". The implementation-defined nature of volatile accesses is unavoidable. If you have, say, a volatile write to a uint32_t variable then most 32-bit or 64-bit systems will implement it as a single write. A 16-bit system will have to break it into two writes. An original Alpha would do a 64-bit read, a modify, then a 64-bit write. Most systems will attempt a single write even if the address is unaligned, others might break it down or the hardware might cause a trap on the unaligned access. Accesses to bitfields have multiple possible implementations. All in all, there's a lot that can't be specified in the standards. Then there are read-write-modify accesses such as "v++;" or "v |= 0x0100;". Some architectures can do these atomically, others would need bus locks and interrupt disabling to be atomic. (As an interesting aside here, in the C standards up to C17 only described "access to volatile objects". Only in C17 did it change to "volatile accesses", defining what was meant by using a pointer-to-volatile cast to access data that had not been defined as volatile. I don't know what the C++ standards say there - but I believe that, as for C, every compiler handles volatile accesses as you might expect.) > reads it ten times, inside that exact loop and nowhere else. > The compiler must not optimize it to one single read or > completely away (because it doesn't see anything changing it). It can still unroll the loop. Similarly if you have : volatile int v; if (a) { v = 1; } else { v = 2; } then the compiler can do: int temp = a ? 1 : 2; v = temp; The run-time pattern of volatile accesses must match the "abstract machine" exactly, but the pattern of the generated assembly need not. > reading the same memory location may give different values at > different times, and writing values there most definitely must > never be optimized away). Yes. And for single-core processors (covering the great majority of small-systems embedded programming), "volatile" is often sufficient in many places where atomics would be needed in general. But you need to be aware of its limitations here - it forms part of the solution, but not necessarily all of it. (The gcc implementation of C11/C++11 atomics, at least in the gcc versions I have looked at, are dangerously wrong for single core embedded systems.) > result isn't being used for anything. (It might not have > optimized it away in the first place, but assigning the > result to a volatile makes sure of that.) Yes, that kind of use is convenient. You also have to ensure that the calculation depends on a volatile variable, not just that the result is written to one. It gives you a more self-contained test than reading an starting input from the command line and printf'ing the result. |
| David Brown <david.brown@hesbynett.no>: Sep 22 09:37AM +0200 On 22/09/2022 01:01, Keith Thompson wrote: > If your code handles errors, always think about what > that error handling will actually do. That's good general advice for all programming - and something many people don't consider deeply enough. Add to it that your error handling code must be tested as well as the rest of your code. I've seen several cases in practice where poor and untested error handling code turned a glitch into a disaster. (I agree with everything in the rest of your post - it's just that your final sentence looked so good as a "tip of the day" !) |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 22 12:02PM +0200 Am 22.09.2022 um 08:56 schrieb David Brown: > What do you think "volatile" does, as a qualifier for accesses? A volatile read or write is usually at least the same as a read or write through a memory_order relaxed. So there are some guarantees you can rely on. > different cores see a consistent picture of the relevant data in memory. > Memory barriers are about ordering of the /local/ view of memory reads > and writes. Fences and barriers mean the same. A fence or barrier makes that changes from a foreign thread become visible to another thread or changes from a thread become visible for other threads. |
| 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