TIL that in C++, you can declare variable in if statement


  • Winner of the 2016 Presidential Election Banned

    I've never actually had the (dis?)pleasure of speaking with him or her.


  • Banned

    @Fox said:

    him or her

    You're racist against gender-fluid people!


  • ♿ (Parody)

    @Gaska said:

    You're racist against gender-fluid people!

    :facepalm: Go over there if your'e going to do that. Yes, you know where.


  • Banned

    @boomzilla said:

    your'e

    You're racist against grammar nazis!



  • @Gaska said:

    The fact I have respect for them doesn't mean I can't find flaws in their products. Though when I think about it, this LIFO thingy is quite neat. But I guess passing parameters larger than a register must have been tricky.

    The same way you'd do it on any other machine. Stick them in memory somewhere and pass a pointer. (Besides, the System/360 was a 32-bit architecture, and in the 1960s, 32-bit integers were enough for anyone, as one might say...)

    And saying "I don't know what it is and I don't care" isn't really "finding flaws in their products", you know.


  • Banned

    @Steve_The_Cynic said:

    The same way you'd do it on any other machine. Stick them in memory somewhere and pass a pointer.

    Except on x86, this "memory somewhere" is directly after (or before, depending how you look at it) your local variables. I believe with all this LIFO stuff, it's a tad more complicated - especially switching between "my" "stack" pointer to "their" "stack" pointer (on x86, it's just adding/substracting SP).

    @Steve_The_Cynic said:

    And saying "I don't know what it is and I don't care" isn't really "finding flaws in their products", you know.

    Neither is not knowing and not caring a disrespect. At least not in the meaning of giving respect to a person. I don't have to know where Polish soldiers fought during WW2 to respect them.



  • @anonymous234 said:

    I'll tell you a secret. If you make all your source code files start with a header including the language version, something like
    //language: C++03

    Should probably be /* language: C++03 */. My understanding is the double-slash comments came from C++, so earlier versions of C wouldn't have them (I don't know if they retrofitted them into modern versions of the C standard). Kind of sucks if your compatibility header isn't compatible with everything you want to support :)


  • Banned

    @Kian said:

    Should probably be /* language: C++03 */.

    Only if you care about C revisions before C99. And if you do, you are a bad person.



  • @anonymous234 said:

    then you could have a program read this and automatically use the right compiler for you! You can even use that to make your language backward compatibile forever and at the same time allow breaking changes to be introduced in the future! WOW!

    I had figured that you could extend the use of extern "C" {} to cover that kind of thing (and maybe even add stuff like extern "C++98" {}) for the same purpose, but yeah, this isn't computer rocket science...



  • The __cplusplus macro is already a thing and you can already check it at compile time and do stuff depending on its value.



  • Sorry, I was outlining a way for a new version of C++ to break backwards compatibility, but give you a way to demarcate blocks of code which are old and won't compile any more. That may not have been clear...



  • Ah, I see what you mean now. But, wouldn't you just demarcate all your code then? What would be the point of it if you have to do it anyway because the code broke?


  • Winner of the 2016 Presidential Election Banned

    Because compiling a program in two separate languages at the same time is the best fix, because you get the best of both worlds.



  • Yeah, it'd probably add a lot of complexity...it just sounds like a nightmare for compiler implementors.


  • Winner of the 2016 Presidential Election Banned

    Or anyone trying to read your code, especially if we take this idea to the next logical step; compiling wholly separate languages together. I think there've been at least three TDWTF articles about some horrific conglomerate of webserver operations combining SQL, PHP, JavaScript, and HTML. Soon we'll have the same thing with C, C++, Python, Ruby, Java, and Pascal.



  • You mean like Objective-C++? That's like four languages in one.


  • Winner of the 2016 Presidential Election Banned

    I've never actually seen Objective-C++, so I don't know. But, maybe? If so, I say we add more languages to our multicompiler. Objective-C++, for one. Oh, of course, Perl, because if we're making code unreadable, obviously Perl. Squirrel! Just because I like the idea of having, somewhere in the user's manual where it outlines all of the languages, one of these that just says "Squirrel!"


  • Banned

    @Fox said:

    some horrific conglomerate of webserver operations combining SQL, PHP, JavaScript, and HTML

    Modern day webdevs have to use these four totally unrelated technologies whether they like it or not. Only PHP is replacable in this set.


  • area_can

    @Rhywden said:

    You used the wrong concept.

    That's a C++14 thing, no?


  • Banned

    C++98 has concepts too - they're just implicit.

    Filed under: I'll never miss a chance to make fun of Go.


  • Winner of the 2016 Presidential Election Banned

    @Gaska said:

    @Fox said:
    some horrific conglomerate of webserver operations combining SQL, PHP, JavaScript, and HTML

    Modern day webdevs have to use these four totally unrelated technologies whether they like it or not. Only PHP is replacable in this set.

    Yes, but squeezing them all into one function call? Seems like a :wtf: to me.



  • @bb36e said:

    @Rhywden said:
    You used the wrong concept.

    That's a C++14 thing, no?

    That'll teach me not to define the namespace.



  • Concepts didn't make it to C++14. They might make it to C++17, but I'm much more interested in Modules than in Concepts. Linking in C++ sucks.



  • @LB_ said:

    Linking in C++ sucks.

    True, but I also hope that these newfangled Modules don't end up breaking the preprocessor, because that would be a shark jump of game ending proportions...
    Filed under: or maybe I'll just start using C11 _Generic with hookers and blackjack instead...



  • You can read/see for yourself in the Clang 3.8 documentation: http://clang.llvm.org/docs/Modules.html


  • ♿ (Parody)

    @Gaska said:

    @Fox said:
    some horrific conglomerate of webserver operations combining SQL, PHP, JavaScript, and HTML

    Modern day webdevs have to use these four totally unrelated technologies whether they like it or not. Only PHP is replacable in this set.

    NoSQL COMPLAIN!


  • Considered Harmful

    Have you considered writing a book about your experiences so far in the hostile IT culture?

    Even though, and this is an if, really, it might detract from how much code you produce?

    But on topic...

    Yes, it would be, to squeeze them together like that would probably need to be a stored procedure (rimshot). But some RDBMS have user-defined functions., so perhaps it could be jammed in a function. Which would then be about the same level of :wtf: as before. However there is also the One Off Case.


  • Considered Harmful

    This post is deleted!

  • Banned

    @boomzilla said:

    NoSQL COMPLAIN!

    It took the industry about a decade to realize PHP is indeed replacable. I don't expect them to realize it about the other three at least until 2020.



  • @Steve_The_Cynic said:

    And they are wrong. But yes, there are architectures out there (IBM is still selling zSystems, you know (1)) that don't have a dedicated hardware stack manipulated with instructions like PUSH and POP. So the activation records for a recursable language like C, C++, or Pascal have to be allocated somewhere other than the hardware stack, and on that particular day, I exhausted that "somewhere" and got a bizarre and not very helpful error message.

    This is fairly universal among RISC architectures, actually. For example, the MIPS-32 family had 32 nominally general registers (two of which are really special-purpose, but described as general purpose for various reasons: $0, which holds a constant value of zero, and $ra, which is used for function linkage), one special--purpose IP register, and two 32-bit floating point registers. While there were conventions for the uses to which the given registers were put, the $sp register was just another 32-bit register, and could be used for any other purpose a coder wanted (at the risk of losing compatibility with existing libraries).

    In MIPS, function calls are usually made using jal (jump and link, which jumps to the location of the function and copies the return value into $ra, the return address register). Arguments are passed using the $a0 through $a3 registers; if there are more than four arguments, you have to pass them on the stack.

    The usual stack frame protocol for MIPS is to first decrement $sp by the amount that you need for the entire stack frame. You then save the frame pointer, $fp, from a zero offset with the new value of $sp as the base. You then add $sp by the amount needed for saving the local variables (that is, whichever of the $s0 through $s7 registers are used) save that to $fp, then use $fp as the base from which to save the the arguments (offsets are $a0 == 8, $a1 == 12, $a2 == 16, and $a3 == 20), as well as the $ra (offset 4), and the save registers (offsets going from -4 on down to -32).

    If your assembler allows you to have symbolic constants (and it should), you can use the following for the offsets:
    [code]#stack offsets
    fp.ra = 4
    fp.a0 = 8
    fp.a1 = 12
    fp.a2 = 16
    fp.a3 = 20
    fp.s0 = -4
    fp.s1 = -8
    fp.s2 = -12
    fp.s3 = -16
    fp.s4 = -20
    fp.s5 = -24
    fp.s6 = -28
    fp.s7 = -32[/code]
    And here is a (relatively) short example of a function using those offset constants:
    [code]
    ###############################

    (char*, object*) read_line(char*)

    Read a line of text into the input buffer and parse it

    ###############################
    read_line:
    addi $sp, $sp, -12
    sw $fp, 0($sp)
    addi $fp, $sp, 0
    sw $ra, fp.ra($fp)
    sw $a0, fp.a0($fp) # a0 == pointer to current position in input buffer
    li $v0, read_string
    syscall
    lw $a0, fp.a0($fp)
    jal parse_object
    lw $a0, fp.a0($fp)
    lw $ra, fp.ra($fp)
    lw $fp, 0($sp)
    addi $sp, $sp, 12
    jr $ra
    [/code]

    (OK, so I cribbed it from an earlier post I wrote on Daniweb some time back; you might want to look at this post and this one if you are actually interested. The code above is from my EMECHS Scheme interpreter, which I wrote as an extended demonstration for one of my former instructors to show his Assembly language class - that's my story and I'm sticking to it.)

    The best part? If you know you aren't going to trash any global registers, or call any other functions, you can skip this entitrely. Most compilers will generate no stack handling for leaf functions in MIPS.

    @Steve_The_Cynic said:

    (2) In keeping with the general theme of "there is nothing new under the sun" that is our modern computing experience, how many people in the audience know that the first example of what we now call a hypervisor ran customer software in 1967?

    Yep, VM/370 (originally called something else, but I CBA to look it up) on the IBM System/370 series .



  • @ScholRLEA said:

    @Steve_The_Cynic said:
    And they are wrong. But yes, there are architectures out there (IBM is still selling zSystems, you know (1)) that don't have a dedicated hardware stack manipulated with instructions like PUSH and POP. So the activation records for a recursable language like C, C++, or Pascal have to be allocated somewhere other than the hardware stack, and on that particular day, I exhausted that "somewhere" and got a bizarre and not very helpful error message.

    This is fairly universal among RISC architectures, actually.


    Sure, for various reasons, but the System/360 architecture is about as far from RISC as you can get without using a high-level language as your assembler.

    I'm not sure at which iteration it was introduced, but certainly on the System/370s, there was an instruction called EDIT, which was a single-instruction int2ebcdic() formatter. You gave it a register and a memory address and it placed an EBCDIC representation of the value in the register at that address.

    @ScholRLEA said:

    Yep, VM/370 (originally called something else, but I CBA to look it up) on the IBM System/370 series .

    Actually, you shouldBA to look it up, because you're wrong. VM/370 was the 1972 reimplementation for the System/370 of the already existing CP-67 that first ran on the 360-67.



  • @Gaska said:

    @Steve_The_Cynic said:
    The same way you'd do it on any other machine. Stick them in memory somewhere and pass a pointer.

    Except on x86, this "memory somewhere" is directly after (or before, depending how you look at it) your local variables. I believe with all this LIFO stuff, it's a tad more complicated - especially switching between "my" "stack" pointer to "their" "stack" pointer (on x86, it's just adding/substracting SP).

    Not exactly. Or do you often pass large(*) objects to functions by value?

    (*) Larger than a register, that is.
    @Gaska said:

    @Steve_The_Cynic said:
    And saying "I don't know what it is and I don't care" isn't really "finding flaws in their products", you know.

    Neither is not knowing and not caring a disrespect. At least not in the meaning of giving respect to a person. I don't have to know where Polish soldiers fought during WW2 to respect them.

    True, but you know they fought somewhere. Your attitude about the technology people is equivalent to saying that their work has so little value that it isn't worth even knowing one sentence ("A zSystem is an IBM mainframe.") about it.

    Remember: the guys who built those IBM mainframes laid the conceptual frameworks that enable us to, for example, have virtualisation today. And they did it fifty years ago. (The very first machine running a hypervisor for customer workloads was a 360/67 running CP-67/CMS in 1967!)

    If you blow off knowing what a zSystem is (an alternative name for the descendants of the System/360, still capable of natively running not-recompiled binaries that ran on those 360/67s - this was a revolutionary idea in the 1960s), you run the risk of thinking that VMware invented virtualisation at the end of the 1990s.


  • Banned

    @Steve_The_Cynic said:

    Not exactly. Or do you often pass large(*) objects to functions by value?

    On IA-32, passing doubles by value was very common. Even in 64-bit era it happens often - vectors (mathematical, not containers), small structs etc.

    @Steve_The_Cynic said:

    True, but you know they fought somewhere.

    And I know those guys at IBM created something.



  • @Gaska said:

    @Steve_The_Cynic said:
    Not exactly. Or do you often pass large(*) objects to functions by value?

    On IA-32, passing doubles by value was very common. Even in 64-bit era it happens often - vectors (mathematical, not containers), small structs etc.

    It's been too long since I worked on an IBM mainframe, so I'll just leave you with a couple of ideas, quite possibly neither of which is related to how it was done:

    1. Split the activation record into one part for the arguments and one part for the local variables.

    2. In the early days of System/360, of course, C didn't even exist yet, and probably neither did B. Fortran and COBOL were co-kings, and while I don't know much about COBOL (aside from the fact that it is (in)famously wordy, I can tell you that Fortran passes arguments by reference, even constants, so unless you have huge argument lists, you can do it all in registers.

    @Gaska said:

    @Steve_The_Cynic said:
    True, but you know they fought somewhere.

    And I know those guys at IBM created something.

    But you refused to even show any trace of "what is this thing Steve is talking about". Without knowing what it was, you rejected it as totally uninteresting, so you didn't even know it was made by people at IBM, and you didn't have the slightest hint of knowledge or interest in the fact that I might have been talking about something of huge importance to the field of computing.

    Sigh. I give up.


  • Banned

    @Steve_The_Cynic said:

    Split the activation record into one part for the arguments and one part for the local variables.

    It still doesn't solve the problem of how to populate one activation record with data from another. But I guess you could keep the new one in some general purpose register, and only actually switch to it when you do the function call. Lots of copying, but I guess it wasn't that important overhead, considering what the computers of the time were used for?

    @Steve_The_Cynic said:

    I can tell you that Fortran passes arguments by reference, even constants, so unless you have huge argument lists, you can do it all in registers.

    And references ruin cache locality. But I guess it wasn't very relevant back in the 60s.

    @Steve_The_Cynic said:

    But you refused to even show any trace of "what is this thing Steve is talking about".

    The LIFO thingy? I did. Every other thing you mentioned? It's irrelevant to the discussion.



  • @Gaska said:

    @Steve_The_Cynic said:
    Split the activation record into one part for the arguments and one part for the local variables.

    It still doesn't solve the problem of how to populate one activation record with data from another. But I guess you could keep the new one in some general purpose register, and only actually switch to it when you do the function call. Lots of copying, but I guess it wasn't that important overhead, considering what the computers of the time were used for?

    Copying? Populate one from the other? Why would you want to do that?

    You just, in effect, have two activation record chains, one for locals and one for arguments, chased through different registers.

    (The alternative interpretation is that you are confusing the abstract idea of an activation record with how you implement it on a machine with a specific "physical" architecture.)

    And computers of the time were used for a wide range of things, with widely varying requirements. Sort of like today.

    @Gaska said:

    @Steve_The_Cynic said:
    I can tell you that Fortran passes arguments by reference, even constants, so unless you have huge argument lists, you can do it all in registers.

    And references ruin cache locality. But I guess it wasn't very relevant back in the 60s.

    No, not so much. And of course they only ruin it for small objects. For large objects, the overhead of copying to the activation record becomes prohibitive. And much of the use of Fortran, of course, calls for passing arrays around, and you don't want to pass them by value.



  • Oh, and on the general topic of activation records: All of the above pales into insignificance next to implementing activation records for full-on Wirth Pascal. (Turbo Pascal's activation records are made less complicated by the prohibition of passing inner procedures/functions to procedures/functions taking procedures/functions as arguments.)

    The problem in Pascal arises from nested function scopes (i.e. I can write a function or procedure inside another, where it will be callable only from places inside the outer one) and what happens when I pass the inner functions as parameters to functions outside the enclosing scope. It's a moderate nightmare, similar in hairiness to how Smalltalk has to handle nested code blocks in its equivalent of if statements and the like.


  • Banned

    @Steve_The_Cynic said:

    Copying? Populate one from the other? Why would you want to do that?

    To pass arguments from one function to another? Or can activation records be shared between multiple stack frames?

    @Steve_The_Cynic said:

    And computers of the time were used for a wide range of things, with widely varying requirements. Sort of like today.

    They weren't used for watching grumpy cats in HD.

    @Steve_The_Cynic said:

    No, not so much. And of course they only ruin it for small objects. For large objects, the overhead of copying to the activation record becomes prohibitive.



  • @Gaska said:

    @Steve_The_Cynic said:
    Copying? Populate one from the other? Why would you want to do that?

    To pass arguments from one function to another? Or can activation records be shared between multiple stack frames?

    Well, that's not exactly what was implied, and you have to copy from one generation of activation record to the next in a machine-stack activation record model as well, so I don't see why this requirement is of any interest whatsoever.
    @Gaska said:
    @Steve_The_Cynic said:
    And computers of the time were used for a wide range of things, with widely varying requirements. Sort of like today.

    They weren't used for watching grumpy cats in HD.

    Well I don't really see why anyone would want to do that anyway.


  • Banned

    @Steve_The_Cynic said:

    Well, that's not exactly what was implied, and you have to copy from one generation of activation record to the next in a machine-stack activation record model as well, so I don't see why this requirement is of any interest whatsoever.

    Let's say I call one function and store its result at first place in stack memory, call another function and store result in next stack position, and then I call a function that takes those two parameters from stack. On x86, I can either copy those variables to a new stack frame, or pretend that those two values always were part of the next stack frame - in the latter case, I completely avoid copying anything.

    @Steve_The_Cynic said:

    Well I don't really see why anyone would want to do that anyway.

    Well, eighteen and a half million people found a reason.



  • @Steve_The_Cynic said:

    The problem in Pascal arises from nested function scopes (i.e. I can write a function or procedure inside another, where it will be callable only from places inside the outer one) and what happens when I pass the inner functions as parameters to functions outside the enclosing scope. It's a moderate nightmare, similar in hairiness to how Smalltalk has to handle nested code blocks in its equivalent of if statements and the like.
    Wrap that scope up in a nice little bundle called a "closure", stick it on the heap, and let the activation record on the stack go, like all the other languages do?



  • @Gaska said:

    @Steve_The_Cynic said:
    Well, that's not exactly what was implied, and you have to copy from one generation of activation record to the next in a machine-stack activation record model as well, so I don't see why this requirement is of any interest whatsoever.

    Let's say I call one function and store its result at first place in stack memory, call another function and store result in next stack position, and then I call a function that takes those two parameters from stack. On x86, I can either copy those variables to a new stack frame, or pretend that those two values always were part of the next stack frame - in the latter case, I completely avoid copying anything.

    You have to be careful with those straw men, you know, they might catch fire. You create the outer function call's stack frame / activation record, call the first inner function, store its result in the activation record, and so on. No copies required on the no-machine-stack version either.
    @Gaska said:
    @Steve_The_Cynic said:
    Well I don't really see why anyone would want to do that anyway.

    Well, eighteen and a half million people found a reason.

    More fool them.


  • Banned

    @Steve_The_Cynic said:

    You have to be careful with those straw men, you know, they might catch fire. You create the outer function call's stack frame / activation record, call the first inner function, store its result in the activation record, and so on. No copies required on the no-machine-stack version either.

    So those records indeed can be shared between multiple stack frames.



  • @Gaska said:

    @Steve_The_Cynic said:
    You have to be careful with those straw men, you know, they might catch fire. You create the outer function call's stack frame / activation record, call the first inner function, store its result in the activation record, and so on. No copies required on the no-machine-stack version either.

    So those records indeed can be shared between multiple stack frames.

    Um, did I say that? NO. And you didn't say it about x86 either. Neither your description of the x86 process nor mine of the no-machine-stack process involves sharing stack frames.

    In the x86 version, you build the "arguments" part of the stack frame for the outer function call piece by piece on the stack as you calculate the values, then you call the function, and it creates the "local variables" part internally.

    In the no-machine-stack version, I allocate the memory I need for the arguments, calculate them one by one, and store them in that memory, then call the function and let it do its own thing.

    The only difference is where and how the arguments part of the stack frame is allocated, either piece by piece on the machine stack or all at once somewhere else.


  • Banned

    Now I'm confused, and I bet you're as confused as I. So I'll try to explain it again as simple as possible.

    Function A takes no parameters and returns single value on stack.
    Function B takes no parameters and returns single value on stack.
    Function C takes two parameters from stack.
    Function D calls C(A(), B()). It doesn't need the results of either A or B for anything but calling C.

    Question: is it possible for function D to not copy the results of A and B anywhere before being able to call C?

    Answer for x86: Yes.
    Answer for IBM zSystem: ???


  • Discourse touched me in a no-no place

    @Gaska said:

    Function A takes no parameters and returns single value on stack.

    I'm just trying to imagine what this would look like. Return values are usually in a register. When they aren't, and the caller didn't allocate space prior to the call for the value to be written into, the first step has to be to copy the value from the holding space into somewhere that isn't likely to be smashed by some other call (including an interrupt-driven syscall, so really can't put this off). Which is desperately unsafe unless you put the holding space into a thread-specific variable instead of on the stack (and that's getting really complicated), so modern ABIs specify that when the returned struct can't be passed in registers (how many? Depends on ABI…) then the caller has to allocate space ahead of time and pass that in for the callee to fill out. The compiler handles this transformation for you.

    It's messy.


  • Banned

    @dkf said:

    I'm just trying to imagine what this would look like.

    It might, for example, look in the way you described in the rest of your post.

    @dkf said:

    Which is desperately unsafe unless you put the holding space into a thread-specific variable instead of on the stack

    Isn't stack thread-specific already?


  • Discourse touched me in a no-no place

    @Gaska said:

    Isn't stack thread-specific already?

    Yes, but anything not in your stack frame/activation record can be smashed when an interrupt happens (e.g., at the end of your timeslice). Caller allocates some stack space is a cromulent and common approach.



  • @Gaska said:

    Now I'm confused, and I bet you're as confused as I. So I'll try to explain it again as simple as possible.

    Function A takes no parameters and returns single value on stack.
    Function B takes no parameters and returns single value on stack.
    Function C takes two parameters from stack.
    Function D calls C(A(), B()). It doesn't need the results of either A or B for anything but calling C.

    Question: is it possible for function D to not copy the results of A and B anywhere before being able to call C?

    Answer for x86: Yes.
    Answer for IBM zSystem: ???


    I think you've fundamentally misunderstood how functions return values on x86. The amount of stack groping A() and B() would have to do to allow this is unrealistic, if you mean "takes no parameters" in an absolute sense (i.e. not even implicit compiler-generated ones). They would have to remove the return address from the stack, push the return value, then put the return address back on the stack, then return. (Or do something daft like pop EBX; PUSH result; JMP EBX)

    If there's an implicit parameter that shows where to put the return value (there are scenarios where this will be the case, like if A() or B() returns a large struct), and that points to what will be the "argument" part of the activation record for the call to C(), then yes, there's no need to copy, but you can do that on a no-machine-stack architecture as well.

    And in reality, functions return register-sized values in registers rather than by hand-waving them onto the stack.

    I'd love to know where you're getting all the straw for this conversation, and why you aren't worried about the fire risk.


  • Discourse touched me in a no-no place

    @Steve_The_Cynic said:

    I'd love to know where you're getting all the straw for this conversation, and why you aren't worried about the fire risk.

    QFFT


Log in to reply