- Why should a "c" programmer learn c++ ? - 3 Updates
- Why should a "c" programmer learn c++ ? (V2) - 15 Updates
- max without ifs - 5 Updates
- Are there any asm-instructions to support OOP - 2 Updates
James Kuyper <jameskuyper@alumni.caltech.edu>: Sep 14 12:16AM -0400 On 9/11/20 2:57 PM, Chris Vine wrote: > On Fri, 11 Sep 2020 11:44:54 -0400 > James Kuyper <jameskuyper@alumni.caltech.edu> wrote: >> On 9/11/20 6:17 AM, Chris Vine wrote: ... > "otherwise, the behavior is undefined" seem to have been read in the > context of arrays only and not applying to raw malloc'ed memory. That > reading is impossible with C++20's [expr.add]/4. ... I don't see that as being the case. It says: "When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P. (4.1) — If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value. (4.2) — Otherwise, if P points to an array element i of an array object x with n elements (9.3.3.4), 76 the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i + j of x if 0 ≤ i + j ≤ n and the expression P - J points to the (possibly-hypothetical) array element i − j of x if 0 ≤ i − j ≤ n. (4.3) — Otherwise, the behavior is undefined." (7.6.6p4). Just like the corresponding words in the C standard, paragraph 4.2 is predicated on P pointing at the i'th member of an array. If there is no such array, then 4.3 applies, and the behavior is undefined. If C++2020 handles this issue better than C does, the different wording that makes that true must lie in some other part of the document. > also that the "effective type" of raw memory is normally the type of > the first object constructed in it (�6.5/6 of C11), so in a sense > malloc'ed memory becomes an array in C by being treated as an array. The fact that this isn't normally the case is precisely what's problematic in C. When an lvalue of a given type is used to write to dynamically allocated memory, that write gives that memory the effective type of that lvalue. If you only write one element of an array at a time, each element of that array acquires an effective type which is the array's element type, but nothing acquires an effective type that is the array type. There are ways around this: you can write an entire struct object to the memory using a single assignment expression, in which case that object acquires that struct type as its effective type, and in particular, if one of the members is an array, that member's memory acquires that array type. Also, when you copy an object into dynamically allocated memory using memcpy(), memmove(), or otherwise copied as an array of character type, there's a special rule that says that the memory acquires the effective type of that object. As a special case of this, an array object can be copied into the memory, giving it an array type. Lots of C code treats dynamically allocated memory as if it were an array, without either of those two special cases applying. Therefore, a strict reading of the rules of pointer arithmetic gives access to any element of such an array other than the first element undefined behavior. > (Somewhat akin to the new C++ implicit-lifetime types.) 6.7.2p12 has code that contains a comment indicating that X *p = (X*)std::malloc(sizeof(struct X)) implicitly creates an object of type X in the allocated memory. 20.10.12p5 appears to be the clause that actually supports that comment. However, it's not clear to me from 20.10.12p5 that X *p = (X*)std::malloc(n*sizeof(struct X)); would implicitly create an n-element array. I certainly wouldn't object to that being the case, but it doesn't clearly say so. > is undefined behaviour because of �6.5.6/8, as in C it is the only way > of getting to the second element in advance of constructing the first > one. I think it is undefined behavior. I don't think it was intended to be undefined behavior, so this represents a defect in the standard, but I don't see any way to justify saying that there's an array that can be used to give meaning to such pointer arithmetic. > taken to arise for the case of an array of trivial types, given that > array types are now implicit-lifetime types. I will need to consider > revised [intro.object]/10 further on this. Could you explain what that section says that makes you feel that way? >> C++2020 different, to render that clause inapplicable? > My meaning was that it was not applicable to my example, which did not > involve construction of an object. Now that I have a copy of n4860.pdf, I see that there's a much more serious obstacle to having that clause apply: there is no such clause in C++2020. The location in n4860.pdf that corresponds to 5.7 in n3797.pdf is 7.6.6, but it contains no such wording, nor does similar wording appear anywhere else. Given "int i;", is there no longer any meaning defined for 1 + &i, or have I missed something that defines it? I've just started reading this version of the standard - I wouldn't be surprised if I missed something. |
Ike Naar <ike@rie.sdf.org>: Sep 14 05:38AM > evaluated only once, and they both define addition of integer values to > pointer values in terms of a containing array; if there's no such array, > there's no applicable definition of what the addition means. If "equivalent" means "identical in value", i++ and i=i+1 are not equivalent. The value of i++ is the value of i before increment. The value of i=i+1 is the value of i after increment. |
James Kuyper <jameskuyper@alumni.caltech.edu>: Sep 14 09:38AM -0400 On 9/14/20 1:38 AM, Ike Naar wrote: > If "equivalent" means "identical in value", i++ and i=i+1 are not equivalent. > The value of i++ is the value of i before increment. > The value of i=i+1 is the value of i after increment. I meant equivalent as a stand-alone expression, such as the one that was being discussed. My purpose was to justify cross-referencing what both standards say about adding integers to pointers, which is not described directly in the paragraphs describing postfix increment; I wasn't interested in going into the other aspects of that operator. If I'd been inclined to discuss postfix increment operators in a more general sense, the simplest way would have been to quote the entire relevant paragraphs: C: "The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The value computation of the result is sequenced before the side effect of updating the stored value of the operand. With respect to an indeterminately-sequenced function call, the operation of postfix ++ is a single evaluation. Postfix ++ on an object with atomic type is a read-modify-write operation with memory_order_seq_cst memory order semantics." C++: "The value of a postfix ++ expression is the value of its operand. [Note: The value obtained is a copy of the original value. — end note] The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type other than cv bool, or a pointer to a complete object type. An operand with volatile-qualified type is deprecated; see D.5. The value of the operand object is modified (3.1) by adding 1 to it. The value computation of the ++ expression is sequenced before the modification of the operand object. With respect to an indeterminately-sequenced function call, the operation of postfix ++ is a single evaluation. [Note: Therefore, a function call cannot intervene between the lvalue-to-rvalue conversion and the side effect associated with any single postfix ++ operator. — end note] The result is a prvalue. The type of the result is the cv-unqualified version of the type of the operand. If the operand is a bit-field that cannot represent the incremented value, the resulting value of the bit-field is implementation-defined." |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Sep 13 09:31PM -0700 Juha Nieminen <nospam@thanks.invalid> writes: [...] > C programmers also seem to be, for some reason, not very intimate > with the newer versions of the C standard, even though they add > relatively little to the language. An ironic statement, considering what comes next. > pointers to the function will point to different places. Declaring > the function as merely 'inline' is the better option because in this > case duplicates are discarded for the final executable.) Despite what you may think, in C 'static inline' is usually a better choice than just 'inline'. There are restrictions on 'inline' functions that do not apply to 'static inline' functions, and, more importantly, simply changing 'static inline' to 'inline' /can/ lead to linker errors - or worse, since using such functions can be undefined behavior. Also, despite what you may think, the rules in C for inline functions (without 'static') are not the same as the rules in C++ for (without 'static') inline functions. |
Real Troll <real.troll@trolls.com>: Sep 14 12:20AM -0500 On 09/09/2020 18:08, olcott wrote: > I spoke with someone on the comp.lang.c recently about the benefits of > c++. Her objections was that c++ has a huge learning curve. One thing c programmers are not prepared to accept is that "they are Cobol programmers of 2030". Linus was discussing this with another Linux developer and they found that young people are not willing to learn C when there are other languages such as C# . So old C programmers have nothing to fear for the next 10 years as they are not likely to face any competition from the young developers. However, the technology is changing so old programmers may not find it easy to adapt to the new requirements. |
olcott <NoOne@NoWhere.com>: Sep 14 12:24AM -0500 On 9/14/2020 12:20 AM, Real Troll wrote: > face any competition from the young developers. However, the technology > is changing so old programmers may not find it easy to adapt to the new > requirements. Yet it seems unwise to create operating systems using C#, yet these same systems could be easily transformed into C with classes, thus overcoming the objecttions of Linus Torvald to C++. -- Copyright 2020 Pete Olcott |
David Brown <david.brown@hesbynett.no>: Sep 14 07:38AM +0200 On 14/09/2020 07:24, olcott wrote: >> likely to face any competition from the young developers. However, >> the technology is changing so old programmers may not find it easy to >> adapt to the new requirements. It makes little sense to write application code in C, in this century - there needs to be extraordinary reason for doing so. But it is still a good choice for a lot of low-level work, embedded work, performance-critical work (in libraries), and some kind of portable work. In particular, with C you can make code that is more "stand alone" and independent of other libraries and ABI details that can cause issues for other languages, including C++ (no matter how little of the language you use). > Yet it seems unwise to create operating systems using C#, yet these same > systems could be easily transformed into C with classes, thus overcoming > the objecttions of Linus Torvald to C++. It would be crazy, IMHO, to use a managed language like C# for an OS. An OS needs to be lower level than that. (Higher level languages can be used for OS services.) C++ works fine for OS's, but the API - the interface between the OS and the rest of the system - is best held in pure C as the most portable interface language. |
Stuart Redmann <DerTopper@web.de>: Sep 14 08:54AM +0200 >> I hadn't come across that little article before. Thanks, it was fun. > Yes, indeed. Sometimes small gems come out of even the most useless > threads! +1 Wanted to say thanks, too. Stuart |
Juha Nieminen <nospam@thanks.invalid>: Sep 14 07:40AM > of thing). Thus the risk of duplication should normally be zero (and if > you have a compiler with a warning like gcc's -Winline, it is actually > zero). Not all compilers are gcc and clang. For example the sdcc compiler (which is relatively widely used out there) will happily instantiate every 'static inline' function in every compilation unit where it's defined, and its linker is not advanced enough to remove these instantiations even if they are never called. I know, because I had to fix this problem in a work-related project (for a target that has 32 kilobytes of space for the executable binary). Removing the 'static' made it work correctly. > definition. If you have used "inline" alone, and need a non-inlined > version, you can't get that from the original inline definition - you > need to re-declare it in a non-inline version. In which situation do you need a "non-line version" of the function? If you need, for example, a pointer to the function, you can create one just fine, and it will work just like in C++. If the compiler needs to actually instantiate the inline function, it will do so, and it will be a unique instantiation across the entire program (ie. there will be no duplicates). If I'm wrong with this, please correct me. |
Juha Nieminen <nospam@thanks.invalid>: Sep 14 07:43AM > Despite what you may think, in C 'static inline' is usually a > better choice than just 'inline'. If you use 'static inline' you may get unwanted duplications of the function implementation. For example the sdcc compiler does this (even when the function instantiation isn't actually called anywhere). Sure, that might not matter much in a multi-gigabyte PC. It starts mattering a lot more when you have eg. 32 kilobytes of space for your executable binary. > functions, and, more importantly, simply changing 'static > inline' to 'inline' /can/ lead to linker errors - or worse, > since using such functions can be undefined behavior. Care to give some examples? |
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 14 12:47AM -0700 On 9/13/2020 10:38 PM, David Brown wrote: > It would be crazy, IMHO, to use a managed language like C# for an OS. > An OS needs to be lower level than that. (Higher level languages can be > used for OS services.) C++ works fine for OS's, probably should not use exceptions in C++ wrt creating an OS? |
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 14 12:48AM -0700 On 9/14/2020 12:47 AM, Chris M. Thomasson wrote: >> An OS needs to be lower level than that. (Higher level languages can be >> used for OS services.) C++ works fine for OS's, > probably should not use exceptions in C++ wrt creating an OS? Well, at least when creating the Kernel. |
"Christian Hanné" <the.hanne@gmail.com>: Sep 14 09:58AM +0200 If someone new to C would learn C++ I'd recommend Visual C++ becuase it's a more visual language. It's rather like Logo. |
David Brown <david.brown@hesbynett.no>: Sep 14 11:34AM +0200 On 14/09/2020 09:40, Juha Nieminen wrote: >> you have a compiler with a warning like gcc's -Winline, it is actually >> zero). > Not all compilers are gcc and clang. Indeed. But gcc is pretty common - and many other compilers (like Intel's on pc's, and CodeWarrior for several Motorola/Freescale/NXP microcontrollers) match gcc semantics and at least some extensions. For many C programmers, whether you think it is a good thing or not, "practical C" means "gcc C". > For example the sdcc compiler (which > is relatively widely used out there) SDCC is not very widely used. It is an impressive project, and I'd be less reluctant to use 8051 and similar cores if SDCC were the industry standard there instead of Keil and IAR, but it is a limited project used for a dying breed of devices. > I know, because I had to fix this problem in a work-related project > (for a target that has 32 kilobytes of space for the executable binary). > Removing the 'static' made it work correctly. SDCC puts a lot of effort into working /correctly/ - even if that does not mean working /efficiently/. Don't get these mixed up. (Though of course in embedded systems, performance or code size can be an absolute requirement too.) Writing code for these kind of microcontrollers invariably requires writing "SDCC 8051 C" or "Keil 8051 C" or "IAR 6800 C". You don't expect more than minimal portability and approximate C standards conformity (SDCC tends to aim a bit more towards standards conformity even at efficiency costs). You expect that different compilers generate wildly different efficiencies for the same code, and that small changes to code that have no semantic difference (and perhaps no object code difference for optimised gcc code on an x86) can make a big difference to the resulting code. Getting good code from a compiler like SDCC is a niche skill - it's fun in its way, but not relevant to a wider discussion of C. > actually instantiate the inline function, it will do so, and it will be > a unique instantiation across the entire program (ie. there will be no > duplicates). Try this on godbolt.org: inline int foo(int x) { return x * 2; } typedef int (*fint)(int); fint foobar(void) { return foo; } You get this code: foobar: mov eax, OFFSET FLAT:foo ret The function "foo" is not created - so linking will fail. You need to define it specifically, in one (and only one) C file, just like any other externally linked function that is used somewhere in the code. If the inline definition is visible, you can do that simply by writing: "int foo(int x);" This is different from C++ - in C++, a non-inlined version of the function "foo" will be generated as needed, from a plain "inline" function. Or in C you can avoid including the "inline" header definition and define a completely different externally linked "foo". (This is not allowed in C++.) With "static inline", the non-inline version will always be generated as needed, but be independent from any other non-inline copies generated in other translation units. (This is true for C and C++.) > If I'm wrong with this, please correct me. The point you are missing, as far as I can see (and I too hope others will correct me if I am wrong), is that C++ supports having an externally linked function generated in multiple object files. This is vital to templates, and is also used for inline functions, static data, and the new weirdly-named "inline variables". So with C++, it's fine for the compiler to generate an externally linked non-inlined "foo" function here, included in many translation units, and it is up to the linker to pick one and throw out the others. (The ODR requires that they all be the same, because there is no way to specify which is kept.) In C, this is not allowed - you cannot have the same externally linked symbol defined in more than one object file in the program. So the C behaviour is different here. (In most cases, of course, these differences don't matter - usually when you have an inline function, you are not planning on having a non-inlined version anyway.) |
David Brown <david.brown@hesbynett.no>: Sep 14 11:36AM +0200 On 14/09/2020 09:43, Juha Nieminen wrote: >> inline' to 'inline' /can/ lead to linker errors - or worse, >> since using such functions can be undefined behavior. > Care to give some examples? You forget you are replying to Tim. He expects you to do your own homework and post the results. You can look forward to a reply in a couple of months, when you have completely forgotten this thread, saying "are you sure about that?". |
David Brown <david.brown@hesbynett.no>: Sep 14 11:38AM +0200 On 14/09/2020 09:47, Chris M. Thomasson wrote: >> An OS needs to be lower level than that. (Higher level languages can be >> used for OS services.) C++ works fine for OS's, > probably should not use exceptions in C++ wrt creating an OS? It is common to disable exceptions (and RTTI) in such low-level C++ code, but I don't see it as an absolute requirement. It depends on the kind of OS you are writing. |
Real Troll <real.troll@trolls.com>: Sep 14 12:55AM -1000 On 14/09/2020 06:24, olcott wrote: > Yet it seems unwise to create operating systems using C#, That's what Microsoft is doing with Windows 10 OS. they have started with the "Store" and some aspects of Windows UI. Rumours are abound that some system files are also rewritten in C#. However, there are more than billion lines of code to be written or re-written using c# so it takes time!!. Microsoft can't hire all the C# programmers to speed up the process. |
Richard Harnden <richard.nospam@gmail.com>: Sep 14 02:17PM +0100 On 14/09/2020 11:55, Real Troll wrote: > than billion lines of code to be written or re-written using c# so it > takes time!!. Microsoft can't hire all the C# programmers to speed up > the process. An IBM man-year .. 730 guys trying to get it done by lunch. |
"Öö Tiib" <ootiib@hot.ee>: Sep 14 05:42AM -0700 I wanted somehow at weekend to find maximum of two int32_t-s without branches (like ifs or ? operators) in C++. Using gcc builtins I was able to write something quite quickly. It looks like C but it compiles as C++ too. #include <inttypes.h> // for int32_t related stuff int32_t mad_max(int32_t a, int32_t b) { int32_t d, result; int32_t o = -1 * __builtin_ssub_overflow( a, b, &d); __builtin_ssub_overflow( a, (o ^ (d >> 31)) & d, &result); return result; } It has no undefined behaviours I hope and where it does compile there it seems to work. Demo: <http://coliru.stacked-crooked.com/a/19eca82b689292e5> However with raw C++ I am in trouble. Does anyone have some idea how to do the trick in raw C++? |
Melzzzzz <Melzzzzz@zzzzz.com>: Sep 14 12:49PM ><http://coliru.stacked-crooked.com/a/19eca82b689292e5> > However with raw C++ I am in trouble. Does anyone have some idea > how to do the trick in raw C++? You have pmaxsd instrunction and I guess intrinsic for x86. No need for this abomination and much more efficient. -- 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 |
Bonita Montero <Bonita.Montero@gmail.com>: Sep 14 02:49PM +0200 #include <cstdint> int f( int32_t a, int32_t b ) { int32_t mask = ~(a < b ? -1 : 0); return a & mask | b & ~mask; } The ternary operation is often substituted by a "sbb regx, regx". Depends on the compiler you use. |
Bonita Montero <Bonita.Montero@gmail.com>: Sep 14 02:53PM +0200 > int32_t mask = ~(a < b ? -1 : 0); > return a & mask | b & ~mask; > } Or better: int f( int32_t a, int32_t b ) { int32_t mask = a < b ? -1 : 0; return a & ~mask | b & mask; } |
"Öö Tiib" <ootiib@hot.ee>: Sep 14 06:01AM -0700 On Monday, 14 September 2020 15:49:51 UTC+3, Melzzzzz wrote: > > how to do the trick in raw C++? > You have pmaxsd instrunction and I guess intrinsic for x86. No need for > this abomination and much more efficient. I did try to ask for C++ not assembler nor compiler builtins. |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Sep 13 09:03PM -0700 Juha Nieminen <nospam@thanks.invalid> writes: [..in an earlier posting..] > even large programs manageable, maintainable and the code > reusable. > Back when OOP was first developed, in the 70's and 80', The purported history is wrong. Modular programming was not a precursor to OOP. Preliminary work on what came to be OOP was done in the early 1960's, by Alan Kay, and independently by Ivan Sutherland with Sketchpad. The programming language Simula had classes, virtual functions, and inheritance in 1967. The earliest mention I'm aware of even of the term modular programming was in 1968, and modules themselves were years later. I was hearing Alan Kay talk about Smalltalk and OOP before the programming language Modula existed. By then Smalltalk had already gone through two iterations, and even the first version, Smalltalk 72, was firmly object-oriented. I won't comment on your description of what OOP is, but certainly there is a sharp contrast with what Alan Kay had to say about Smalltalk, generally recognized as the canonical object-oriented language: Though Smalltalk's structure allows the technique now known as data abstraction to be easily (and more generally) employed, the entire thrust of its design has been to supersede the idea of data and procedures entirely and to replace these with the more generally useful notions of activity, communication and inheritance. Alan Kay, 1972 |
Juha Nieminen <nospam@thanks.invalid>: Sep 14 07:26AM > more generally useful notions of activity, communication and > inheritance. > Alan Kay, 1972 That doesn't sound contradictory to what I described. He's describing higher-level notions of OOP, namely how programs ought to be designed, how "objects" should be thought of and handled. In other words, rather than think of "objects" as being just data containers, they should be tought of as entities that are interacted with and which behave in a particular manner. In other words, data abstraction, ie. data hiding. (In other words, the actual data inside the object, and the way it's implemented, is hidden behind a more abstract public interface that tells the object *what* to do, not *how* it's being done.) I, however, was talking about how all that is actually implemented behind the scenes. In other words, how all the data members of a class are physically (well, in "physically" in terms of their location in RAM) gathered within an object. In other words, an "object" is in practice, for all intents and purposes, a C struct with a layer of compile-time abstraction on top of it. I suppose that from the higher-level perspective there's nothing stopping the practical implementation from gathering the data members in some other manner (such as putting the same members of all objects in one common array), but that's not how OO programming languages do it, because it has never been very practical from an implementation perspective. The problem with this is that, while this type of OO implementation was just fine and dandy in the 70's, 80's and largely the 90's, nowadays it leads to inefficient code due to how processor architectures have advanced. CPUs like consecutive memory accesses and dislike memory accesses that jump in large steps, or randomly. How OOP has been implemented inevitably leads to the latter, which is bad for performance. Another issue is that modern processors like to see in advance where the program execution is going to go. If they don't see it, or if they "guess" it wrong, it causes a performance penalty. Virtual functions largely work against this, causing even more performance hits. The other modern advance is in compilers. While they do their best, typical OOP implementations oftentimes hinder compiler optimizations, eg. when it comes to autovectorization. It's relatively easy to show practical examples of this. |
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