edward@runxtsa.runx.oz.au (Edward Birch) (08/12/90)
A friend of mine works with a large organization where they have defined a standard that "variables must be declared where first used". I think that this is an absolutely insane standard for the following reasons: o The declarations clutter the algorithm. o Most C and C++ programmers are used to seeing variables declared in one place. I believe that changing this will only add to maintenance costs and development time. C has had the ability to declare variables at the start of each block. How often is it used ? Why isn't it used ? o I have found that declaring variables where they are first used significantly adds to the development time of code. With little benefit to the over all presentation of the code. o I also find that declaring variables at the start of functions in one spot to significantly adds to the readability of the code. I would be extremely interested in feedback. Edward Birch Phone: +612 2 958-2119 UUCP: seismo!munnari!runx.oz!edward ACSnet: edward@runx.oz ARPA: edward%runx.oz@seismo.css.gov CSNET: edward@runx.oz -- Telex: 10713856 BRHT UUCP: seismo!munnari!runx.oz!edward ACSnet: edward@runx.oz ARPA: edward%runx.oz@seismo.css.gov CSNET: edward@runx.oz
gregk@cbnewsm.att.com (gregory.p.kochanski) (08/13/90)
In article <2161@runxtsa.runx.oz.au> edward@runxtsa.runx.oz.au (Edward Birch) writes: >A friend of mine works with a large organization where they have defined a >standard that "variables must be declared where first used". > >I think that this is an absolutely insane standard for the following reasons: Well, I'm not a true expert, but I've been using that coding style in both C and C++ for several years voluntarily, and I like it. It makes you think about just where you need this data, and there's less flipping around in the text editor looking for the declaration, as it's often on the same screen. I suppose it's a matter of taste. Of course, if you have small functions, this becomes a moot point. Greg Kochanski gpk@physics.att.com AT&T Physics Research -- Physics for fun and profit.
zmls04@trc.amoco.com (Martin L. Smith) (08/13/90)
We have just started to move from C to C++ and the declare-at-usage style still looks a little strange. I've noticed a tendency in my own code to declare throwaway variables (the index in a loop, say) when they are used but to group more significant ones in declaration chunks, which are not always at the start of a function block. I've noticed at least two drawbacks in this style: 1 - It's cowardly and has no really clear principles. 2 - Declaring loop indices at point of use leads to some problems in my case, at least, because I tend to reuse i, j, etc, and C++ doesn't like repeated declarations (at least in the first clause of a for-loop). The second for loop that uses 'i', for example, should not declare it. If I later delete the first loop then I have to remember to add the declaration to the new first loop (the old second loop, that is [are you still there?]) or I get a compile error. This symptom strikes me as an indication that this style is not the best we could do. Anyhow, I too would really like to read more discussion on this issue. -- Martin L. Smith Amoco Research Center P.O. Box 3385 zmls04@trc.amoco.com Tulsa, OK 74102 [zmls04@sc.msc.umn.edu] 918-660-4065
bmcphers@alias.UUCP (Brent McPherson) (08/14/90)
In article <2161@runxtsa.runx.oz.au> edward@runxtsa.runx.oz.au (Edward Birch) writes: >A friend of mine works with a large organization where they have defined a >standard that "variables must be declared where first used". > >I think that this is an absolutely insane standard for the following reasons: >[reasons deleted] I have to disagree with you. There are important reasons for not doing this in C++. If all variables in C++ were declared at the start of a routine, then all the constructors/destructors would get executed on each invocation of the routine (even if the variables are not used because of control flow within the routine). The problem is also worse because of nested constructors and destructors with inherited classes. A better plan would be to declare all variables at the start of each block so objects that aren't used (depending on which blocks are executed) don't have their constructors/destructors called. This distributes the cost evenly over the entire routine and can result in less overhead in most cases (rather than a large, constant cost at entry/exit of the routine). I also like having variables declared close to where they are used since you will be less likely to forget to remove old variable declarations as the code evolves. This is not so much of a problem these days since most compilers will warn of unused/uninitialized variables. Finally, common sense should prevail. If a variable is first assigned a value within a loop it should be declared outside the loop just in case the compiler decides to create the variable on the stack for each loop iteration. BTW. I think constant cursoring to the top of a routine to add/delete/modify variables increases development time. Also, readability/understandability are a matter of personal preference (remember the brace/format/spacing debates between various C programmers). >Edward Birch -- Brent McPherson Alias Research Inc. bmcphers%alias@csri.toronto.edu
jimad@microsoft.UUCP (Jim ADCOCK) (08/14/90)
In article <2161@runxtsa.runx.oz.au> edward@runxtsa.runx.oz.au (Edward Birch) writes: >A friend of mine works with a large organization where they have defined a >standard that "variables must be declared where first used". > >I think that this is an absolutely insane standard for the following reasons: .... I believe the major goal should be to initialize where declared. Since some initialization values are computed down-stream in a function, some declarations have to placed down-stream. The general rule my group uses is to move declarations into the smallest scope reasonable. If one keeps one's functions small, then any declaration strategy should work. If a variable is used through-out a function, continue to declare that variable at the start of the function.
ark@alice.UUCP (Andrew Koenig) (08/15/90)
In article <2161@runxtsa.runx.oz.au> edward@runxtsa.runx.oz.au (Edward Birch) writes: > A friend of mine works with a large organization where they have defined a > standard that "variables must be declared where first used". `Must' is too strong, but I do think there is often an advantage to declaring a variable at its first use. For example: void f() { int i; // a bunch of code if (i > 0) g(); // ... } Is the test `i > 0' valid or not? The answer is that it's valid if and only if something in `a bunch of code' initialized `i.' That is, during at least part of `a bunch of code,' variable `i' exists but is uninitialized. If `i' were initialized at its point of declaration, it would never be uninitialized. In other words, if you initialize all your variables when you declare them, they won't be uninitialized. Now one might argue that this is just a matter of taking care to get it right. After all, uninitialized variables aren't always dangerous -- you just have to make sure not to use them until you've initialized them. But many classes initialize things for you: String s; // ... s = "hello"; With every String class I've seen, `s' will be initialized to the null string at its declaration. Later that initial value must be discarded and replaced with "hello" . It is almost always faster to say // ... String s = "hello"; As to the argument that it's harder to find declarations if they're scattered through your functions: if your function is so big that it matters, you should probably split it up anyway. -- --Andrew Koenig ark@europa.att.com
gwu@nujoizey.Berkeley.EDU (George Wu) (08/16/90)
In article <2161@runxtsa.runx.oz.au>, edward@runxtsa.runx.oz.au (Edward Birch) writes: |> A friend of mine works with a large organization where they have defined a |> standard that "variables must be declared where first used". |> |> I think that this is an absolutely insane standard . . . I must agree with you. We have no such standard, and so I see all kinds. I find that the interspersed declarations really do cloud the issue. And especially when you have multiple programmers working together, you MUST take maintenance issues into consideration. I find myself screaming when I am debugging someone else's code, I want to find the declaration of a variable, and I find myself forced to wade through the code. This is made worse by the fact that often the variables are defined somewhere in the class hierarchy. But I, of course, still have to parse all the damn code. The only helpful points are that most people at least try to limit the length of routines, and modern editors can do string searches. As for the practice of declaring variables inside a block, ie. within a pair of curly braces, I used to do so to minimize the scope of the variable and clearly designate the variable for use only under certain conditions. For example, I might do something like: switch (fooCode) { case BAR: { char *ascii; // some complex mess ... } case BAZ: { char *ebcdic; // ... } } HOWEVER, I soon discovered that I was doing so because the bloody routine was getting too large. The excess size of the routine is what made restricted scoping use advantageous. The REAL solution was to break down the routine into smaller routines. George ---- George J Wu | gwu@tcs.com or ucbcad!tcs!gwu Software Engineer | 2121 Allston Way, Berkeley, CA, 94704 Teknekron Communications Systems, Inc.| (415) 649-3752
jimad@microsoft.UUCP (Jim ADCOCK) (08/17/90)
In article <1990Aug13.182226.24141@alias.uucp> bmcphers@alias.UUCP (Brent McPherson) writes: >Finally, common sense should prevail. If a variable is first assigned a value >within a loop it should be declared outside the loop just in case the compiler >decides to create the variable on the stack for each loop iteration. Hm, I'm not sure I understand what you're trying to say here. In the following code: for (int i=0; i<1000; ++i) { FOO foo(i); foo.DoSomething(); } It is certainly not the case that a thousand foos reside simultaneously on the stack. Rather, it is guaranteed that an object is destroyed as its constructor is jumped back over. Thus, while conceptually there are a 1000 foos, only one is in existance at any point in time, and they all reside at the same location on the stack.
mat@mole-end.UUCP (Mark A Terribile) (08/17/90)
> A friend of mine works with a large organization where they have defined a > standard that "variables must be declared where first used". > I think that this is an absolutely insane standard for the following reasons: Let me add my voice to those who have have supported this standard (which ought to be a guideline rather than a standard.) > o The declarations clutter the algorithm. The declarations usually look just like an assignment with the type name to the left of the assignment expression. In the case of types which must be initialized with multiple constructor arguments, you probably cannot declare the variable ahead of time. > o Most C and C++ programmers are used to seeing variables declared > in one place. I believe that changing this will only add to > maintenance costs and development time. > C has had the ability to declare variables at the start of each > block. How often is it used ? Why isn't it used ? I worked for several years on a medium-sized (150 000) project that had gone through many years of maintenance and enhancement, including a port from assembler into C. One of the things that kept the code clear and reasonably safe between the occasional rewrites of various parts of the code (which usually occurred when developers ganged up on management and testified that they couldn't review the code well enough otherwise) was keeping all variables in the smallest scope possible. It's done, it works, and it saves a great deal of time in debugging and code review. It also ensures that someone won't accidently use a variable that was used to hold an intermediate value in some small computation when they want to use one of the variables that describes the environment in which the function is operating. Reducing the scope of variables to the minimum necessary reduces the number of needless symbols in scope at any given time; if functions exceed about 16 lines, this starts to make a big difference in safety during maintenance and enhancement. It helps to show how the `long- range' communications represented by variables are tied to the `flow-of- control' (Governs-Invokes) structure of the program. And yes, we had functions that were several hundred lines long and that would have been very difficult to split. (In C++, with class scope as an instance record, it would have been easier.) These functions typically broke down into five or six more or less linear steps, each of which was fairly complicated and had a number of special rules that were invoked if certain conditions occurred in previous steps. Being able to declare variables at the point where their initial values became available would have made this code much safer. So would consts. (Only about a dozen functions were this long; most were between ten and forty lines.) > o I have found that declaring variables where they are first used > significantly adds to the development time of code. With little > benefit to the over all presentation of the code. I found otherwise, both on small projects and on that largish piece of very- real-world code. (It's running in several tens of thousands of copies of a business PBX.) Adopting this style improved the review process; the combination of an informal (but conscientious) review process and code style improvements such as this reduced the number of trouble reports that came back as new bugs by probably a factor of four. > o I also find that declaring variables at the start of functions > in one spot to significantly adds to the readability of the code. I find otherwise IF the code starts out highly readable. This means (among other things) that related computations are grouped and set aside from other computations by white space. If your code reads like good prose, in which each paragraph develops one idea in a more or less linear way, with each computation using the results just computed and providing results that are used immediately thereafter, the inserted declarations (which are usually just type names) will state clearly that this is a new datum holding just this value, not the revision of some previous value. That the datum exists just to hold this value can be reinforced by making it a const . -- (This man's opinions are his own.) From mole-end Mark Terribile
sysop@tlvx.UUCP (SysOp) (08/19/90)
In article <ZMLS04.90Aug13091620@gpss31.trc.amoco.com>, zmls04@trc.amoco.com (Martin L. Smith) writes: > We have just started to move from C to C++ and the declare-at-usage > style still looks a little strange. I've noticed a tendency in my own > code to declare throwaway variables (the index in a loop, say) when > they are used but to group more significant ones in declaration > chunks, which are not always at the start of a function block. > I've noticed at least two drawbacks in this style: > > 1 - It's cowardly and has no really clear principles. Wouldn't the principal be that it's easier to program on-the-fly (er, so to speak)? It's not so bad putting everything at the top if I'm in an editor that easily lets me enter into different windows.... > > 2 - Declaring loop indices at point of use leads to some problems in my > case, at least, because I tend to reuse i, j, etc, and C++ doesn't > like repeated declarations (at least in the first clause of a > for-loop). The second for loop that uses 'i', for example, should > not declare it. If I later delete the first loop then I have to remember > to add the declaration to the new first loop (the old second loop, > that is [are you still there?]) or I get a compile error. This symptom > strikes me as an indication that this style is not the best we could do. I'm just starting to learn C++, and in making very simple programs, I ran into this very problem! Part of me really likes the convenience, but it seems to me that in the 2nd loop, that if I say "for (int loop=0..." and it is already declared, as long as the delarations match (they're both ints), it might as well use the same variable. (Am I missing something?) I mean, it's hard to argue that that would be a casual thing to do, since declaring at first use is casual anyway. It seems a pretty small matter, but as a bug, it bit me enough for me to remember it. On a related topic, in C, you can declare a variable anytime you open a brace ("{"). This seems like a neat idea, since code within braces is more likely to move as a unit. (To get around the problems, I guess you would surround a for loop with a set of braces external to that, then put the declaration, and then the for, but by the time you do all that, it just clutters things up, so why bother?) I guess if it's too messy, you could avoid it, like avoiding the goto, or at least list situations where it's not so messy. > > Anyhow, I too would really like to read more discussion on this issue. > -- > > Martin L. Smith Amoco Research Center > P.O. Box 3385 > zmls04@trc.amoco.com Tulsa, OK 74102 > [zmls04@sc.msc.umn.edu] 918-660-4065 -- Gary Wolfe, SYSOP of the Temporal Vortex BBS ..uflorida!unf7!tlvx!sysop, ..unf7!tlvx!sysop@bikini.cis.ufl.edu
drk@athena.mit.edu (David R Kohr) (08/19/90)
In this discussion of the benefits and drawbacks of declaring variables at their point of use in a block in C++, rather than at the beginning of a block as is required in C, I don't recall anyone making the following point: you can't jump (using "goto") past a declaration with an initializer unless that declaration is within a block nested within the current block. (This is according to Stroustrup, _The_C++_Programming_ Language_, p. 296, section 9.11 of the C++ reference manual chapter; I assume it's still true in AT&T C++ 2.0, but I don't have any reference for that version on hand.) There are those of us who occassionally use "goto" for efficiency reasons (or even to make the code clearer). It seems awkward and error-prone to me to have to consciously avoid jumping over declarations with initializers each time one uses a "goto", and to have to check that each declaration with an initializer is not in a code segment which can possibly be skipped over by a "goto". Such awkwardness does not lend itself to making code more maintainable. This subtle mistake probably does not appear very often in practice, since the "goto" is not used with great frequency in C++, but when it does appear it is probably fairly difficult for the programmer to detect, much like use of an uninitialized variable. However, compilers can easily detect instances of the use of uninitialized variables, while detecting the "goto" error requires a more extensive analysis of the code's flow-of-control. -- David R. Kohr M.I.T. Lincoln Laboratory Group 45 ("Radars 'R' Us") email: DRK@ATHENA.MIT.EDU (preferred) or KOHR@LL.LL.MIT.EDU phone: (617)981-0775 (work) or (617)527-3908 (home)
barmar@think.com (Barry Margolin) (08/19/90)
In article <56642@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: >In article <1990Aug13.182226.24141@alias.uucp> bmcphers@alias.UUCP (Brent McPherson) writes: >>Finally, common sense should prevail. If a variable is first assigned a value >>within a loop it should be declared outside the loop just in case the compiler >>decides to create the variable on the stack for each loop iteration. >It is certainly not the case that a thousand foos reside simultaneously >on the stack. Rather, it is guaranteed that an object is destroyed as >its constructor is jumped back over. Thus, while conceptually there >are a 1000 foos, only one is in existance at any point in time, and they >all reside at the same location on the stack. However, the compiler might decide to grow and shrink the stack frame each time the variable is created and destroyed. Also, if the constructor and/or destructor do anything expensive (e.g. use "new" and "delete" on members of the object) this will be done each time through the loop, when it probably would be OK to reuse a single object. -- Barry Margolin, Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
jamshid@walt.cc.utexas.edu (Jamshid Afshar) (08/21/90)
In article <1990Aug19.160307.20972@athena.mit.edu> drk@athena.mit.edu (David R Kohr) writes: > >In this discussion of the benefits and drawbacks of declaring variables >at their point of use in a block in C++, rather than at the beginning >of a block as is required in C, I don't recall anyone making the >following point: you can't jump (using "goto") past a declaration with an >initializer unless that declaration is within a block nested within the >current block. (This is according to Stroustrup, _The_C++_Programming_ > >There are those of us who occassionally use "goto" for efficiency >reasons (or even to make the code clearer). It seems awkward and >error-prone to me to have to consciously avoid jumping over declarations >with initializers each time one uses a "goto", and to have to check >that each declaration with an initializer is not in a code segment which >can possibly be skipped over by a "goto". Such awkwardness does not >lend itself to making code more maintainable. [text deleted] I hope this isn't starting a goto debate, but... If the program is jumping into the middle of code that has variables (even if they are not used) active that have not been initialized, the program is _already_ not very maintainable (IMHO). Don't get me wrong, I'm not an anti- goto fanatic. I am using them in a part of my code (at least until C++ gets exception handling) because a non-goto version of the function would look like a hack. I have found the opposite to be true: localizing the scope and delaying the definition of a local variable makes the code much more maintainable. The reason is, if I'm goto'ing _back_ in the code, it destroys any variables decalared after the label I'm jumping to. This way, I don't have any old stuff hanging around. If I jump forward, I don't want to jump somewhere unless all active variables at that place have been initialized. Basicly, I believe if goto's are being used to improve program clarity, delaying local variables declaration until point of use (which may be necessary if they are being initialized) and localizing the scope of variables as much as possible make the use of goto's safer and more understandable to a person reading your code. Besides, the argument that "it makes a program with goto's hard to maintain", seems a little like an oxymoron or something. >of an uninitialized variable. However, compilers can easily detect >instances of the use of uninitialized variables, while detecting the "goto" >error requires a more extensive analysis of the code's flow-of-control. I'm not too sure what you're saying here... Compilers already give an error if you goto past an object declaration. I don't really understand why it's easier to detect uninitialized variables as opposed to skipping variable declarations. Let me know if I misinterpreted what you said. PS: I was really thrilled when I discovered C++ goto's take care of calling destructors. setjmp/longjmp don't, do they? Anybody know when exception handling and templates will be implemented (esp. on PC's)? Will the ANSI C++ standard have them? Finally, does everybody know about comp.std.c++? It's really quiet. --Jamshid Afshar --jamshid@ccwf.cc.utexas.edu
roger@procase.UUCP (Roger H. Scott) (08/22/90)
In article <2161@runxtsa.runx.oz.au> edward@runxtsa.runx.oz.au (Edward Birch) writes: >A friend of mine works with a large organization where they have defined a >standard that "variables must be declared where first used". > >I think that this is an absolutely insane standard for the following reasons: > > o The declarations clutter the algorithm. This sounds like a completely subjective, personal opinion. Is it intended to be anything more? > o Most C and C++ programmers are used to seeing variables declared > in one place. I believe that changing this will only add to > maintenance costs and development time. > C has had the ability to declare variables at the start of each > block. How often is it used ? Why isn't it used ? Who cares what "most C programmers" are used to? Most C programmers are used to slowly producing large volumes of unmaintainable code. As far as your "belief", again I ask if this is meant to be anything more than a subjective personal opinion? Could you share your reasononing with us? > o I have found that declaring variables where they are first used > significantly adds to the development time of code. With little > benefit to the over all presentation of the code. Have you really done a controlled experiment where the only significant difference in coding technique pertained to location of declarations? > o I also find that declaring variables at the start of functions > in one spot to significantly adds to the readability of the code. > >I would be extremely interested in feedback. It seems that you must be more interested in "what identifiers are declared within a function" than in "what is the actual lifetime and usage of each identifier". I, for one, and *much* more interested in the latter than the former. Generally speaking, I consider an uninitialized variable declaration to be distracting and counter-useful. It is often the case that the uses of any given variable within a function are all relatively close together, although they may be relatively far from the "top" of the function. In these cases I definitely find it easier to write, understand, and maintain code that declares the variables where they are first used. I will go one step further in this direction and say that I wouldn't mind the ability to un-declare a variable after I am "done" with it so that someone else (or me) looking at the code later doesn't have to scan down to the end of the scope to make sure there isn't some other, later use being made of this oh-so-local variable.
roger@procase.UUCP (Roger H. Scott) (08/22/90)
In article <1990Aug13.182226.24141@alias.uucp> bmcphers@alias.UUCP (Brent McPherson) writes: >... >Finally, common sense should prevail. If a variable is first assigned a value >within a loop it should be declared outside the loop just in case the compiler >decides to create the variable on the stack for each loop iteration. If your compiler is really dumb enough to do that for an ordinary C-sort of variable you need to get a new compiler. The only case where I can imagine that such a thing would be worth restructuring my code over is declarations of automatic objects with constructors and/or destructors - it is unfortunate that C++ compilers won't be smart enough to optimize such things in the forseeable future.
purtill@morley.rutgers.edu (Mark Purtill) (08/24/90)
roger@procase.UUCP (Roger H. Scott) writes: >I will go one step >further in this direction and say that I wouldn't mind the ability to >un-declare a variable after I am "done" with it so that someone else (or me) >looking at the code later doesn't have to scan down to the end of the scope >to make sure there isn't some other, later use being made of this oh-so-local >variable. There is a way to do this; in fact, it was there in "plain" C; just declare the variable in a local group: int function() { /* ... */ { int a_very_local_variable ; /* use of a_very_local_variable */ } /* no uses of a_very_local_variable */ } This isn't eactly what you want (you can't overlap variables like: int a_very_local_variable ; ... int another_very_local_variable ; ... (undeclare a_very_local_variable) ; ... (undeclare another_very_local_variable) ; ... ) but it's still very useful sometimes when ones function is getting rather long, but can't be split easily. ^.-.^ Mark Purtill purtill@dimacs.rutgers.edu (201)932-4580 (O) ((")) P.O. Box 1179, Rutgers Univ., Piscataway, NJ 08855 (201)220-6905 (H) (Above information effective around 25 August 1990)
mjv@objects.mv.com (Michael J. Vilot) (08/24/90)
Roger Scott mentions: > I wouldn't mind the ability to un-declare a variable after I am "done" I use blocks to achieve this effect. I also rely more on expressions and less on explicit intermediate variables. I sometimes use objects of classes that only provide constructors and destructors. I use them only for their side-effects. Here's a simple example: class Timer { time_t start; public: Timer(); // records start ~Timer(); // computes elapsed, displays it on cerr }; void f() { // do some work { Timer t; // do some timed work, and report the time at block exit } // etc. } The Timer object `t' is only active in the inner block. Of course, the object is apparently never used, so I may get into trouble with those building aggressively-optimizing compilers. I hope such optimizations allow this use of constructor/destructor side-effects. -- Mike Vilot, ObjectWare Inc, Nashua NH mjv@objects.mv.com (UUCP: ...!decvax!zinn!objects!mjv)
sdm@cs.brown.edu (Scott Meyers) (08/24/90)
In article <883@zinn.MV.COM> mjv@objects.mv.com (Michael J. Vilot) writes: | I sometimes use objects of classes that only provide constructors and | destructors. I use them only for their side-effects. Here's a simple | example: ... | { | Timer t; | | // do some timed work, and report the time at block exit | } ... | The Timer object `t' is only active in the inner block. Of course, the | object is apparently never used, so I may get into trouble with those | building aggressively-optimizing compilers. | | I hope such optimizations allow this use of constructor/destructor | side-effects. This doesn't even always work in cfront 2.0, as I found out recently when I tried implementing a class to automate routine tracing. Scott
jimad@microsoft.UUCP (Jim ADCOCK) (08/28/90)
In article <41673@think.Think.COM> barmar@think.com (Barry Margolin) writes: >In article <56642@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: >>It is certainly not the case that a thousand foos reside simultaneously >>on the stack. Rather, it is guaranteed that an object is destroyed as >>its constructor is jumped back over. Thus, while conceptually there >>are a 1000 foos, only one is in existance at any point in time, and they >>all reside at the same location on the stack. > >However, the compiler might decide to grow and shrink the stack frame each >time the variable is created and destroyed. Also, if the constructor >and/or destructor do anything expensive (e.g. use "new" and "delete" on >members of the object) this will be done each time through the loop, when >it probably would be OK to reuse a single object. Hm, do you know of any C++ compiler that actually grows and shrinks a the stack within a routine? [in the absense if alloca()s] A while back I read a survey of C compilers that could identify no such compiler. It turns out gotos in the language makes doing such problamatic. Bottom line in any case: if an object's construction and assignment work more or less identically, you can just put the constructor outside the loop, and assign inside the loop. If an object's constructor and assignment work considerably differently, you might need to move the constructor inside the loop.
jimad@microsoft.UUCP (Jim ADCOCK) (08/28/90)
In article <883@zinn.MV.COM> mjv@objects.mv.com (Michael J. Vilot) writes: |The Timer object `t' is only active in the inner block. Of course, the |object is apparently never used, so I may get into trouble with those |building aggressively-optimizing compilers. | |I hope such optimizations allow this use of constructor/destructor |side-effects. Yes, such usages of objects for the side effects of their constructors and destructors is explicetly allowed in the language, and will not be removed by compiler optimizations. Other common examples are objects that act as locks, and objects that print out debugging info as a side effect of construction or destruction.
bright@Data-IO.COM (Walter Bright) (08/30/90)
In article <56953@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: <In article <41673@think.Think.COM> barmar@think.com (Barry Margolin) writes: <<However, the compiler might decide to grow and shrink the stack frame each <<time the variable is created and destroyed. <Hm, do you know of any C++ compiler that actually grows and shrinks a <the stack within a routine? [in the absense if alloca()s] A while back <I read a survey of C compilers that could identify no such compiler. There *are*, however, compilers that allocate variables on top of each other if their live ranges do not intersect. This has the same effect as adjusting the stack. func() { { int i; .... } { int j; .... } } i and j's live ranges do not overlap, so they can (and do with some compilers) share the same storage location. <It turns out gotos in the language makes doing such problamatic. True if you actually are adjusting the SP, but not in the above scheme.
jimad@microsoft.UUCP (Jim ADCOCK) (09/05/90)
In article <2675@dataio.Data-IO.COM| bright@Data-IO.COM (Walter Bright) writes: |In article <56953@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: |<In article <41673@think.Think.COM> barmar@think.com (Barry Margolin) writes: |<<However, the compiler might decide to grow and shrink the stack frame each |<<time the variable is created and destroyed. |<Hm, do you know of any C++ compiler that actually grows and shrinks a |<the stack within a routine? [in the absense if alloca()s] A while back |<I read a survey of C compilers that could identify no such compiler. | |There *are*, however, compilers that allocate variables on top of each |other if their live ranges do not intersect. This has the same effect |as adjusting the stack. | | func() | { { int i; .... } | { int j; .... } | } | |i and j's live ranges do not overlap, so they can (and do with some compilers) |share the same storage location. | |<It turns out gotos in the language makes doing such problamatic. | |True if you actually are adjusting the SP, but not in the above scheme. Yes, but C++ defines that destruction of named objects be on exit from scope [with few exceptions], thus greatly restricting a compilers from performing these optimizations on other than primitives. Temporary objects would be fair game for these optimizations, though....
ark@alice.UUCP (Andrew Koenig) (09/05/90)
In article <57167@microsoft.UUCP>, jimad@microsoft.UUCP (Jim ADCOCK) writes: > | func() > | { { int i; .... } > | { int j; .... } > | } > |i and j's live ranges do not overlap, so they can (and do with some compilers) > |share the same storage location. > Yes, but C++ defines that destruction of named objects be on exit from > scope [with few exceptions], thus greatly restricting a compilers from > performing these optimizations on other than primitives. But in this example, the scopes of `i' and `j' end at the close braces at the end of their respective lines. Therefore a C++ implementation is certainly entitled to let `i' and `j' share storage, as long as it destroys `i' before constructing `j.' -- --Andrew Koenig ark@europa.att.com
bothner@sevenlayer.cs.wisc.edu (Per Bothner) (09/08/90)
jimad@microsoft.UUCP (Jim ADCOCK) writes: >| func() >| { { int i; .... } >| { int j; .... } >| } >| >|i and j's live ranges do not overlap, so they can (and do with some >|compilers) share the same storage location. > >Yes, but C++ defines that destruction of named objects be on exit from >scope [with few exceptions], thus greatly restricting a compilers from >performing these optimizations on other than primitives. Huh? "On exit from scope" means "at the end of the block" in this case, so there is no problem allocating i and j to the same location, even if their class(es) have destructors. Construction and destruction must by definition delimit an object's live range. Two variables in the same function, but defined in non-intersection blocks do not have overlapping live ranges. And can therefore share storage locations. -- --Per Bothner bothner@cs.wisc.edu Computer Sciences Dept, U. of Wisconsin-Madison
jimad@microsoft.UUCP (Jim ADCOCK) (09/12/90)
In article <11190@spool.cs.wisc.edu> bothner@sevenlayer.cs.wisc.edu (Per Bothner) writes: |jimad@microsoft.UUCP (Jim ADCOCK) writes: |>| func() |>| { { int i; .... } |>| { int j; .... } |>| } |>| |>|i and j's live ranges do not overlap, so they can (and do with some |>|compilers) share the same storage location. |> |>Yes, but C++ defines that destruction of named objects be on exit from |>scope [with few exceptions], thus greatly restricting a compilers from |>performing these optimizations on other than primitives. | |Huh? "On exit from scope" means "at the end of the block" |in this case, so there is no problem allocating i and j |to the same location, even if their class(es) have destructors. | |Construction and destruction must by definition delimit an |object's live range. Two variables in the same function, |but defined in non-intersection blocks do not have overlapping |live ranges. And can therefore share storage locations. Sorry for the confusion. I thought it was obvious that when I was talking about constructors/destructors--that I couldn't be talking about the above "int" examples. [Ints, and other built-ins can use destructor syntax under 2.1+ compilers -- but those destructors do nothing, and so do not change traditional optimization issues] Yes. two objects in two non-overlapping scopes in a routine are guaranteed to have only one in existance at any moment, allowing their space on the stack to be shared. My claim is simply that this is not common programming practice. Common practice is for most objects to be declared in the outer -most scope of a function, where their destructors are not called until function exit. Thus, the compiler doesn't get a chance to optimize based on lifetime of most C++ objects. I claim that optimizing based on the lifetime of objects in stack isn't a big issue anyway. The real issue is optimizing based on the lifetime of objects in register. Reusing a register saves time and code space. Reusing a stack slot only saves a little on the frame size.