Hacker News new | comments | show | ask | jobs | submit login

What do you find so disagreeable about collecting variables at the top of a function? For the most part, I like having all the variable declarations at the top, so it's easy to see what names are in what scope.



  void up_front_decls()
  {
    float some_var;
    int another_var;
    
    some_var = get_some_var();
    do_some_calculations(some_var);
    maybe_something_else(&some_var);
    
    some_var = get_another_var();
    do_some_other_calculations(another_var);
    blah_blah_already_broken();
  }

  void as_needed_decls()
  {
    float some_var = get_some_var();
    do_some_calculations(some_var);
    maybe_something_else(&some_var);
    
    int some_var = get_another_var();
    // compile-time error
    // ...
  }

Also:

  void poor_style()
  {
    up_front            declarations;
    also                encourage;
    this_ridiculous    *block_style;
    that_is             a_royal_pain;
    to                  maintain;
    because_some_long_type inevitably;
    screws_it           up;
  }


Finally got the silly regex. To align poor_style in vim with the tabularize plugin:

:Tab /\S\s\zs[ ]\S*

Explanation: \S\s* finds a non whitespace character followed by as many whitespace characters as possible. This brings us to the beginning of the variable name. We then use \zs which says that the "found" area should only begin here.

Since we want the * in block_style to be attached to the indented word but not before the variable name, we match either * or space followed by a non-whitespace character to symbolize the beginning of the word. End result:

  void poor_style()
  {
    up_front                declarations;
    also                    encourage;
    this_ridiculous        *block_style;
    that_is                 a_royal_pain;
    to                      maintain;
    because_some_long_type  inevitably;
    screws_it               up;
  }


I would argue that if declaring up-front versus declaring as-needed makes a significant difference in readability, then your functions are too long.


Except for loop counters. Being able to do:

  for (int i = 0; i < len; ++i) { ... }
vs.

  int i;
  // Half a dozen lines
  for (i = 0; i < len; ++i) { ... }
makes a big difference to me.


Regarding poor_style(), I've never understood this objection (and indeed I prefer the block style you complain about there). Can't your editor fix this up for you in a few keystrokes? This is the sort of thing that an editor should make easy.


I have a few objections:

1. It wastes my time. Sure, I could probably set up my editor to fix this, but I shouldn't have to do so to satisfy someone else's pointless indentation fetish. I've personally never worked on a team where this was an accepted, general guideline. It was always just one guy who wanted this, and did it to every function he touched, adding maintenance headaches for everyone else (until/unless other people finally told him to stop).

2. It messes up diffs. Now instead of one line showing up in the diff, the entire block is often different. And yes, most diff tools have options to hide whitespace differences. Again, though, this adds overhead to everyone who doesn't want this block style. I'd rather not hide whitespace differences, because if someone has added a bunch of inappropriate whitespace (or mangled the block while trying to reformat it to include their new variable), I want to know about it during the code review so I can tell them to fix it then rather than finding it later when I'm editing the file.

3. It doesn't actually help anything. Yes, you get a nice column that shows you all the variable names. What good is that, though? Unless you're putting everything at the top of the function (which has its own set of problems), you're not really getting anything useful from this except maybe prettier code (arguable), because at a glance you still don't really get know all the in-scope variables (not to mention file-scope variables). Moreover, you actually lose something valuable with this style, because now it's harder to determine a variable's type. You're trying to scan left from the name across some indeterminate amount of whitespace to match with the type. This is not typically easy to do, which is why column-oriented data is typically displayed with alternating background colors on each row.


I bet that some programmers' brains need those orderly columns in order to be able to parse the code effectively, overwhelmed by the chaos of rivers of whitespace otherwise. In that case, it sounds like an editor that displays in columns but stores in compact form would be helpful.


That's certainly possible, and I agree with your idea that those devs should use the custom editor setup they need instead of expecting everyone else to accommodate them.


Your up fornt example isn't a result of poor style it's the result of a bad programmer. Assigning the right values to the right variables is the most basic of programming concepts. Sure typo's and bugs happen, I've done it too but it's still a programmer error not style error.


Any style that encourages errors is a bad style. Yes, it's possible to make the code correct and still use up-front declarations. It's also possible to make the code correct while using a 10k-line main() littered with gotos. It's still a very poor programming style.

People make mistakes. Practices should be built around this fact, not built assuming people could be perfect if they just tried a little harder.


Have you ever seen a bug caused by this? I haven't. At least anecdotally, this is purely stylistic, and has no impact outside of personal preference.

With one caveat: Heavy constructor/destructor use requires it.


Yes. I have absolutely seen bugs caused by the wrong variables being assigned to. I've seen it especially with loop variables. jaegerpicker indicates that he's also caused bugs by assigning to the wrong variables.

Not sure what you mean about "heavy constructor/destructor use" requiring this style.


Declaring at the top of a function conflicts with the C++ convention of powerful constructors. It's perfectly normal for a C++ class to allocate a lot of memory, open a network socket, or do any other number of heavy-weight things during construction. If you declare all variables at the top of the function, you're forced to have C-style init functions for all data structures that you call separately.


This is not JavaScript, where a variable defined inside a function always has the entire function as its scope, and so it's confusing not to have it on top. In C++, a variable is not in scope until it's declared, so you can just declare it when you're going to start using it.

If your function is five pages long, and in the fifth page you can't remember what's in scope and you have to reread through the entire function above, it might seem that having all variable declarations at the top would help, because you'd only have one place to look at. But the real problem there is that the function is too long, and it should be refactored instead.


Even in Javascript's case I don't like putting variables on top. Declaring variables only as they are needed lets JSHint warn me if I use accidentally something outside of its intended scope.


Among other things already mentioned, it makes it impossible to be "const pedantic" as Mr. Carmack professes to now be, i.e. declaring everything that is only initialized and then never changed as const.

I most certainly am const pedantic as well, and I was back when I worked on this project, too -- quite a few of the "inconsequential" lines I checked in were simply moving a variable declaration down to where it was initialized, and adding 'const'. I found the resulting code much easier to read.


Doing so encourages you to have mutable data.

If instead, a variable is assigned to only when it is defined, you're moving (in a small way) towards functional programming.

Also, I often use scope just control the lifetime of a resource. These look like meaningless braces in the middle of a function to the uninitiated. It's RAII.


I was reading '21st Century C' (I strongly recommend it for anyone writing C on a regular basis), and the author also argued against declaring all the variables up front. I don't quite know why, but I had a very strong reaction against it. As far as best practices go, I would try to keep every function small enough that you can find all the stack variables easily. If the function gets too hairy, refactor it so you can follow where the variables are going.


I don't see any advantages of declaring variables up front. Not all variables should be at the function scope. Often I have a local variable that is only used inside one branch of a conditional, why should I declare that at the top? Declaring things up front also separates the type from the usage, which makes things harder to read and takes up twice as many lines in some cases.


Maybe I miscommunicated; I'm not against declaring variables at scopes smaller than the function - I declare variables at the top of conditional branches as well. I still don't buy the 'twice as many lines' reason for mixing assignment and declaration.


You should be using const variables as much as possible (and in general preferring immutable data). In that case assignment and declaration _must_ take place at the same location.


this


There's an upvote button to agree with a comment, no need to post a single word. Generally, terse answers aren't really appreciated here (unless they're very good).


you can find all the stack variables easily

You can't do that unless you look at the assembly output. While there may have once been a time in history where "locally-scoped variable == entry on stack", those days are long gone. Any decently smart compiler (i.e. GCC and LLVM) will use registers in preference to the stack, and will collapse local variables whose lifetimes do not overlap into a single storage location.


My bad, I meant locally scoped, I tend to think in terms of "it's on the stack (or a register), or it's on the heap (because it's been malloc'd and needs to be freed)". I do need to review my terminology...


The only time you actually need to declare variables up front nowadays is if you have a goto that would otherwise skip over stack-allocated variable declarations. It's only a problem on certain compilers, but there are enough of them that it's best to keep variable declarations up front in such cases.


It makes it difficult to have const locals if you get some sort of dependency chain based on other locals interspersed with other computation. For example, if you're a const nazi you can't have code like this:

    const int x = foo();
    // ...
    const int y = bar(x);
and also have your variables all declared at the top.

In C++ I'm a const nazi and declare where used to facilitate it, but if I'm writing C that targets the MSVC compiler (C89) where all variables have to be collected at the top of the inner scope I'll relax this as much as I have to.


Besides the other points made it can prevent usage of const.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: