- "C++ on the Move" by Darryl K. Taft - 19 Updates
- Is this safe? - 1 Update
- Order of addresses of string-literals inside a translation unit - 1 Update
- Visual Studio 2022 still not fit for purpose - 4 Updates
gst <g.starck@gmail.com>: Mar 13 11:49PM -0700 Le lundi 13 mars 2023 à 18:02:00 UTC-4, Paavo Helde a écrit : > For example, a string variable in Python is kind of immutable AFAIK. Pure strings, as floats, ints,.. in Python *are* totally immutable. not kind of. totally. > atomic/scalar value and any references to it only serve as a tool for > memory optimization and cannot be used for modification of data visible > via other references. they don't acts like immutable, they are. You cannot modify a string/immutabe objects. they do not have any method with side effect. you can assign a string to something, then that thing can be passed down the line/anywhere, the same string can be assigned to many different things/names by many assignements (many references to them yes). But you cannot, never, modify a string in-place, with any reference to it. It has no such method (even private)/feature/possibility. You could only (try to) modify it by accessing C low level internal of CPython say, the implementation, not the language. but by then it's not anymore Python you're doing and that could/would break at some point your/any Python code obviously. |
David Brown <david.brown@hesbynett.no>: Mar 14 09:08AM +0100 On 13/03/2023 23:01, Paavo Helde wrote: >>> code if the values of variables never changes. >> That I didn't know. I thought immutable variables were a pretty >> unique part of Erlang. No. It is like mathematics - a mathematician will be happy with "x = 1", but get really upset if you say "x = x + 1" :-) Keeping things immutable makes it far easier to reason about what code does, and what transformations or optimisations are safe. You get to move things around, do things in parallel, and get much more structured and modularised code when you can't have different bits of the code all affecting the values of bits of data. Of course it also leads to limitations if you can never change the value of a variable. Applied to C++ programming, it is often good to aim where possible to have your data and your methods "const", or even "constexpr". But C++ supports a wide range of programming paradigms, so use what suits best for the task in hand. (There are "impure" functional languages, like ocaml, that allow mutable variables.) > memory optimization and cannot be used for modification of data visible > via other references. > At least that's what I've gathered, I'm no expert in Python. The key to understanding Python, IMHO, is to realise that there are no variables. There are only named references - and most of the things they point to, are immutable. So in Python, if you write : x = 1 you are /not/ creating an integer variable "x". You are creating an immutable object with type "integer" and value "1", unless such an object already exists (in which case you are increasing its reference count). You then have the reference named "x" that points to it. When you then write : x += 1 you are not changing "x". You are (roughly speaking) applying the methods x.__assign__(x.__add__(1)). This results in an object of integer type and value 2 being created, and "x" is now pointed to it. The reference count of the object "1" is decreased - if it is 0, the object can be garbage collected. |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 01:17AM -0700 > The standard defines "undefined behavior" as "behavior for which > this document [the standard] imposes no requirements". I recommend > not using that particular phrase with a different meaning. Wholeheartedly agree. A lot of people think "undefined behavior" is synonymous with "might behave in an unpredictable fashion". That supposed equivalence is simply wrong, and there should never be any ambiguity about its wrongness. |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 01:21AM -0700 > immutable atomic/scalar value and any references to it only serve as a > tool for memory optimization and cannot be used for modification of > data visible via other references. String values are immutable, but a string value is not a variable. The variable can be changed, only the string value is immutable. > At least that's what I've gathered, I'm no expert in Python. Ditto. |
David Brown <david.brown@hesbynett.no>: Mar 14 09:55AM +0100 On 13/03/2023 20:53, Keith Thompson wrote: > The standard defines "undefined behavior" as "behavior for which this > document [the standard] imposes no requirements". I recommend not using > that particular phrase with a different meaning. I had thought my meaning was clear, but you are absolutely right to recommend being more precise here. There's been a certain amount of misunderstanding and miscommunication in this thread already - I should not make it worse by lazy wording! |
Muttley@dastardlyhq.com: Mar 14 09:26AM On Mon, 13 Mar 2023 18:43:11 -0000 (UTC) >to reason about the code's correctness. For example, concerning recursion, >I have heard arguments that claim it helps to avoid off-by-one mistakes >that can happen pretty easily in languages such as C. Quite possibly, but recursion has its own issues. The main one being that the dev forgets to code in the correct stop checks and one day it recurses forever. Sure that can happen with a standard for loop, but its more likely to be spotted. >Erlang processes are isolated from each other, and can communicate only >using message passing. I remember reading about some kind of Actor Model >and maybe it's also supposed make code correctness easier to achieve. But Message passing is theoretically pure but horribly inefficient. >book about algorithms, look up what you want, and then implement it >in Erlang. Erlang is of course Turing complete, so it *is* doable >but not straightforward. Its difficult to know when one has been steeped in traditional development style since god was a boy, but I think traditional programming is much closer to how people think. Eg "Add 1 to this, go around again. Put this value in a box and put the box in that other box" etc. Functional (and declarative) programming requires you to learn to think in a different way. |
Muttley@dastardlyhq.com: Mar 14 09:31AM On Mon, 13 Mar 2023 20:17:41 +0100 >C++ never need to be changed after their initialisation or first >assignment, and it is much easier to reason about the correctness of the >code if the values of variables never changes. One argument in favour of using #define over const vars. >The motto for Erlang is "write once, run forever". It's most fun >feature, IMHO, is the extent to which you can replace code while >everything is still running. The idea is that you don't have to stop Which is probably about as accurate as javas write once run everywhere motto. |
kalevi@kolttonen.fi (Kalevi Kolttonen): Mar 14 09:51AM >>feature, IMHO, is the extent to which you can replace code while >>everything is still running. The idea is that you don't have to stop > Which is probably about as accurate as javas write once run everywhere motto. Concerning "write once": if we are realists, we must admit that no large program can be written "once" without any need for later bugfixes or enhancements. So "once" in this context cannot be taken quite literally. However, hot code updating feature certainly contributes to the latter part of the motto. Concerning "run forever": I tried to find evidence, but did not find any. Anyway, I have heard a rumour of some Erlang service being available for 10 years straight. br, KK |
Muttley@dastardlyhq.com: Mar 14 09:56AM On Tue, 14 Mar 2023 09:51:08 -0000 (UTC) >Concerning "run forever": I tried to find evidence, but did not find >any. Anyway, I have heard a rumour of some Erlang service being >available for 10 years straight. Possibly, but I'm just naturally suspicious of any provider who boasts of 24/7/365 uptime because in the real world that simply isn't possible. Whether its erlang, Tandom NonStop or cloud, eventually things go wrong and the services goes offline even if only for a short time. |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Mar 14 11:04AM +0100 On 2023-03-14 7:49 AM, gst wrote: >> memory optimization and cannot be used for modification of data visible >> via other references. > they don't acts like immutable, they are. You cannot modify a string/immutabe objects. they do not have any method with side effect. Oh they do. In particular CPython concatenation modifies the string's buffer when it is exclusively owned, reference count 1. That reduces n concatenations from O(n^2) to O(n), which every Python programmer should know about, not to mention programmers in general. > you can assign a string to something, then that thing can be passed down the line/anywhere, the same string can be assigned to many different things/names by many assignements (many references to them yes). But you cannot, never, modify a string in-place, with any reference to it. It has no such method (even private)/feature/possibility. See above. But if you modify your assertions just a little, and talk about /observable buffer changes/, i.e. not even time consumption, then they could be OK. > of CPython say, the implementation, not the language. but by then > it's not anymore Python you're doing and that could/would break at > some point your/any Python code obviously. That's right, but that's not necessary; ordinary concatenation will do. - Alf |
gst <g.starck@gmail.com>: Mar 14 03:26AM -0700 Le mardi 14 mars 2023 à 06:05:04 UTC-4, Alf P. Steinbach a écrit : > buffer when it is exclusively owned, reference count 1. That reduces n > concatenations from O(n^2) to O(n), which every Python programmer should > know about, not to mention programmers in general. heuh yes, ok, but this an implementation detail of CPython which does not allow/enable you to modify an already assigned string. > But if you modify your assertions just a little, and talk about > /observable buffer changes/, i.e. not even time consumption, then they > could be OK. what could be ok ? I don't modify any of the above assertions, there will be never buffer changes to any Python assigned string : ``` orig_ref = "foobar" any_function(orig_ref) assert orig_ref == "foobar" # always ``` |
kalevi@kolttonen.fi (Kalevi Kolttonen): Mar 14 10:30AM > 24/7/365 uptime because in the real world that simply isn't possible. Whether > its erlang, Tandom NonStop or cloud, eventually things go wrong and the > services goes offline even if only for a short time. VMS operating system has had stable clustering available for decades now, and I suppose the initial releases were long before Unix had any kind of clustering. Check this out: https://en.wikipedia.org/wiki/VMScluster "Cluster uptimes are frequently measured in years with the current longest uptime being at least sixteen years" Whether the services running on top of VMS were also continuously up is not clear based on that article. br, KK |
David Brown <david.brown@hesbynett.no>: Mar 14 01:30PM +0100 > the dev forgets to code in the correct stop checks and one day it recurses > forever. Sure that can happen with a standard for loop, but its more likely to > be spotted. It is rarely difficult to be sure your recursion ends - you just have to make sure you have a loop variant that gets smaller with each round, and ends at 0. Very often you are looping over a data structure (such as a list), and this happens naturally. Of course, you might /want/ infinite recursion. Many (not all) functional programming languages support lazy evaluation, and so infinite lists are commonly used. >> using message passing. I remember reading about some kind of Actor Model >> and maybe it's also supposed make code correctness easier to achieve. But > Message passing is theoretically pure but horribly inefficient. That depends heavily on the implementation, and the efficiency you are considering. Message passing can give you lock-free algorithms, or close to lock-free, and can significantly simplify a lot of the rest of the code in comparison to shared memory systems. It can scale better, and work smoothly across different levels of parallelism (light-weight fibres, threads, processes, multiple cores, multiple computers). But it can be costly if you need to share large blocks of data back and forth between multiple actors. > style since god was a boy, but I think traditional programming is much closer > to how people think. Eg "Add 1 to this, go around again. Put this value in a > box and put the box in that other box" etc. It is far more natural to think "Do this for each box", or "Do this for the first box. Then do the same for all the other boxes" than to think "First count the number of boxes. Then make a new counter, starting at 1. Repeatedly look at the counter, find the box that matches that number, and do this it. Then add one to the counter, and check if now matches the count of the boxes - if so, stop." > Functional (and declarative) > programming requires you to learn to think in a different way. Yes. Functional programming is often quite natural for people with a stronger background in mathematics. It has more of a focus on describing the results you want, rather than the details of how you get there (for /efficient/ implementations in functional programming, you often want to describe intermediary results along the way rather than hoping the compiler is a genius!). I think that the more experienced you are with one style, the more it will feel "natural" for you and the harder it is to get used to an alternative. That applies to all sorts of things, not just programming. But there's little doubt that some kinds of algorithms or programming problems are easier and clearer in one style of programming language, while others are easier in a different style. |
David Brown <david.brown@hesbynett.no>: Mar 14 01:41PM +0100 >> assignment, and it is much easier to reason about the correctness of the >> code if the values of variables never changes. > One argument in favour of using #define over const vars. No. You can't change a variable declared "const" after initialisation. (There are undefined behaviours that can appear to change the value, such as via pointer casts, but then it's still easy to reason about the code correctness - it is incorrect.) >> feature, IMHO, is the extent to which you can replace code while >> everything is still running. The idea is that you don't have to stop > Which is probably about as accurate as javas write once run everywhere motto. I can't answer for Java, but Erlang's code replacement works if you write your code in the appropriate manner - as people do, for serious Erlang applications. |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 08:57AM -0700 > Personally I understood the concepts of OO within probably 10 mins > of encountering it, functional programming didn't take much longer > to grok [...] That's incredible! > I must have programmed in at least 10 languages in my life but the > one I could never get my head around was Prolog and I still can't. > [...] On the other hand this statement is very credible. |
Muttley@dastardlyhq.com: Mar 14 04:16PM On Tue, 14 Mar 2023 13:30:15 +0100 >make sure you have a loop variant that gets smaller with each round, and >ends at 0. Very often you are looping over a data structure (such as a >list), and this happens naturally. Its not that simple especially when head vs tail recursion matters. >Of course, you might /want/ infinite recursion. Many (not all) >functional programming languages support lazy evaluation, and so >infinite lists are commonly used. If you can infinite recursion then its not recursion, its a disguised loop. >fibres, threads, processes, multiple cores, multiple computers). But it >can be costly if you need to share large blocks of data back and forth >between multiple actors. Generally its more complex and slower than a monolithic approach which is why despite all the endless academic papers and books written about the wonders of message passing kernels, few actually exist in the wild. >> box and put the box in that other box" etc. >It is far more natural to think "Do this for each box", or "Do this for >the first box. Then do the same for all the other boxes" than to think Except its not that. Its do this for the first box, then jump in the box and do it for the 2nd box. Then jump in that box. And when you get to the end you have to jump out of all the boxes in reverse russian doll style. Whereas iterating through some kind of list is done in everyday life. >But there's little doubt that some kinds of algorithms or programming >problems are easier and clearer in one style of programming language, >while others are easier in a different style. No doubt true. |
Muttley@dastardlyhq.com: Mar 14 04:20PM On Tue, 14 Mar 2023 08:57:25 -0700 >> of encountering it, functional programming didn't take much longer >> to grok [...] >That's incredible! Why? When I first learnt C++ it was analogous to C with structs with function pointers in them. Inheritance is hardly complicated and neither is polymorphism. Though if I'd encountered C++17 having never seen C++ before I probably wouldn't have bothered and would have gone down the Python or Java route instead. As for functional programming - its recursion + value based polymorphism. >> one I could never get my head around was Prolog and I still can't. >> [...] >On the other hand this statement is very credible. Go patronise someone else you plank. |
David Brown <david.brown@hesbynett.no>: Mar 14 05:49PM +0100 >> ends at 0. Very often you are looping over a data structure (such as a >> list), and this happens naturally. > Its not that simple especially when head vs tail recursion matters. "Tail recursion" is when the end of a function is a recursive call that can be replaced by a jump to the start (or near the start) of the function. It's an optimisation which is extremely important to efficiently implementing linear recursive code. I'm not sure what you mean by "head recursion", nor why you think optimisation affects the likelihood of a developer accidentally writing unbounded recursion. Maybe you mean something different by the terms? >> functional programming languages support lazy evaluation, and so >> infinite lists are commonly used. > If you can infinite recursion then its not recursion, its a disguised loop. In terms of computational theory, all loops are disguised recursion - loops can /always/ be re-written in terms of recursion. And /simple/ recursion can be re-written in terms of loops (but more complicated cases cannot). A functional programming definition such as : nums = 1 : map (+1) nums is infinite recursion, generating an infinite list. It doesn't matter if it could be replaced by a loop or not - it is still infinite recursion. Or you could write : data T = Branch T T ts = Branch ts ts That defines an infinite recursive binary tree structure - not a useful one, but if that is a disguised loop, it is well disguised! > Generally its more complex and slower than a monolithic approach which is why > despite all the endless academic papers and books written about the wonders of > message passing kernels, few actually exist in the wild. What, exactly, is a "monolithic approach" to synchronisation of actors, or to sharing of data? Message passing systems of many kinds are found all over the place - it is not a concept for kernels. Some languages (like Erlang, Go, occam) have it as fundamental aspects of the language. There are libraries and frameworks using it. There is hardware that is based around message passing (such as XMOS devices). There are OS kernels build around it - it is the foundation for FreeRTOS, the most popular real-time OS. Pretty much everything that is considered "distributed" works by message passing. I suspect you are thinking of a few specific use-cases where shared memory with locks is more efficient than message passing might be (and such cases certainly exist), and generalising from there. > do it for the 2nd box. Then jump in that box. And when you get to the end you > have to jump out of all the boxes in reverse russian doll style. Whereas > iterating through some kind of list is done in everyday life. You are seriously abusing the analogies here. Perhaps you should have spent more than 10 minutes looking at functional programming languages, because you seem a bit confused. >> problems are easier and clearer in one style of programming language, >> while others are easier in a different style. > No doubt true. There's always /something/ we can agree on :-) |
Muttley@dastardlyhq.com: Mar 14 05:14PM On Tue, 14 Mar 2023 17:49:20 +0100 >function. It's an optimisation which is extremely important to >efficiently implementing linear recursive code. I'm not sure what you >mean by "head recursion", nor why you think optimisation affects the Head recursion: int func(int val) { ret = func(val); .. do something .. return ret; } Tail recursion: int func(int val) { ... do something ... return func(val); } I'm sure there are plenty of examples in google. >loops can /always/ be re-written in terms of recursion. And /simple/ >recursion can be re-written in terms of loops (but more complicated >cases cannot). Sounds unlikely. All recursion AFAIK can be emulated using a loop and one or more stacks. >A functional programming definition such as : > nums = 1 : map (+1) nums No idea what thats doing. > ts = Branch ts ts >That defines an infinite recursive binary tree structure - not a useful >one, but if that is a disguised loop, it is well disguised! I imagine the interpreter is looping internally so unless it uses magic how does it do it? >> message passing kernels, few actually exist in the wild. >What, exactly, is a "monolithic approach" to synchronisation of actors, >or to sharing of data? Shared memory and locking obv. >Message passing systems of many kinds are found all over the place - it >is not a concept for kernels. Some languages (like Erlang, Go, occam) Occam was a joke though sadly it lived long enough to give Guido bad ideas about whitespace. >passing (such as XMOS devices). There are OS kernels build around it - >it is the foundation for FreeRTOS, the most popular real-time OS. If you say so. Every embedded OS I've ever worked on has been linux, usually BusyBox. >Pretty much everything that is considered "distributed" works by message >passing. Err yes, but they're generally not in a single application binary are they which is what we're discussing. >You are seriously abusing the analogies here. Perhaps you should have >spent more than 10 minutes looking at functional programming languages, >because you seem a bit confused. I'm not confused at all. I know how recursion works thanks. Perhaps you don't? |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 09:47AM -0700 > Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes: [what does it mean for a function to "meet its specification"?] > So lets' call them all "procedures" for the moment and get back to > the question at hand. Should a specification be taken to be > essentially minimal? I would say so. I don't exactly agree with this view. Certainly there are some kinds of behaviors (in the general sense of the word) that are implied and meant to be understood as part of what is required for how a function behaves (again in the general sense of the word), but not so much that the specification should be taken to be "essentially minimal". Consider for example the C library function qsort(), as specified in the ISO C standard. I'm sure most people expect that qsort() mustn't do "anything more" than sort the array, e.g., it must not change the state of an input file, or change an unrelated byte of memory, etc. But there are no requirements on "invisible" behaviors such as how fast the code runs or how much memory is taken. We might be disappointed if qsort() uses an O( N**3 ) algorithm, but that disappointment is not because the specification is not met. Also compare with the C++ standard. When there are runtime requirements of O(1) or O(N) or O(N log N), etc, the C++ standard states these bounds explicitly; they are not implied or tacitly assumed. By the way, qsort() is an interesting example because of expected memory usage. I think most people would not be surprised to learn that qsort() uses (in some implementations) O(log N) stack space. But this amount of memory is not necessary. There are, IIANM, sorting algorithms that are O(N log N) in runtime and O(1) in memory usage. Does a qsort() implementation that uses a standard quicksort algorithm violate the "essentially minimal" threshold? I think most people would agree that it meets the specification given in the C standard. I guess my stance on this is that the burden falls on whoever is responsible for writing the specification. Any needed conditions must be stated explicitly, somewhere, at least informally. We should never have to rely on the reader having the same set of builtin assumptions that the author has. |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 08:48AM -0700 > has undefined behavior is what makes storing multiple string > literals in overlapping memory workable. We're really saying the > same thing, from two different points of view. Your earlier statement has causality going in one direction, and mine has it going in the opposite direction. I'm hard pressed to see how those two views can be thought of as the same thing. |
Mr Flibble <flibble2@reddwarf.jmc.corp>: Mar 14 08:01AM On 13/03/2023 20:27, Bonita Montero wrote: >> The code is not wrong; ... > It is because your type is not conditionally used as with my example. > Your both compilers are just more tolerant than MSVC. Nope. g++ and clang correctly discard the statement (as it is a template) so the code compiles. By not discarding the statement MSVC is not compliant with standard C++ so the defect report is valid. /Flibble |
Mr Flibble <flibble2@reddwarf.jmc.corp>: Mar 14 08:02AM On 13/03/2023 20:27, Bonita Montero wrote: >> that doesn't alter the fact that the code in my defect report is well >> formed and VS2022 is wrong to reject it. ... > MSVC is corrent because your denendent type doesn't exist. Nope. g++ and clang correctly discard the statement (as it is a template) so the code compiles. By not discarding the statement MSVC is not compliant with standard C++ so the defect report is valid. /Flibble |
Bonita Montero <Bonita.Montero@gmail.com>: Mar 14 03:35PM +0100 Am 14.03.2023 um 09:01 schrieb Mr Flibble: > Nope. g++ and clang correctly discard the statement (as it is a > template) ... The error is thrown when the template is instntiated. |
Bonita Montero <Bonita.Montero@gmail.com>: Mar 14 03:35PM +0100 Am 14.03.2023 um 09:02 schrieb Mr Flibble: > Nope. g++ and clang correctly discard the statement > (as it is a template) so the code compiles. ... The error is thrown when the template is instntiated. |
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