Ifs and &&s and Plan 9's Source Code
computationallyendowed.com
computationallyendowed.com
It's not hacky, it's actually completely well structured. Even debugging info for C/C++ macros is there and done properly.
None of this is new. DWARF2 (or 3 or 4), the standard debugging format for all non-MS compilers, has been able to do this well since 1993.
In Clang/LLVM, for example, nodes are annotated with debug information using LLVM's metadata system. However, it is strictly optional for any given transformation to preserve the metadata on IR nodes. So what makes it through after optimizations is kind of a "best effort" affair.
Not all compilers even really degrade. GCC does post-hoc variable tracking (and thus is mostly unaffected by optimization except when values are optiized out completely). For declarations and statements, the info is always on the declaration, so it won't be lost.
Both compiler guarantee that at -O0, all debug info will be kept.
Honestly, even if you had to hit an 'expand this statement' button in your debugger to see `if x() && y()` spread into:
x_val = x()
if x_val
y_val = y()
if y_val
...
it would completely remove the necessity to write strange things to get around this limitation. Why do we have compilers and a huge variety of languages if not to stop writing strange things unnecessarily?On the other hand, an optimizing compiler already makes it pretty hard to single-line-step through a program (what with reordering, CSE, and more sophisticated transforms). Single-expression-stepping would be an even more difficult "debugging illusion" to provide.
On the consumer side, knowing where you are is actually the least hard problem in optimized debugging, compared to things like tracking variables that got split up into multiple disjoint registers, or part in register/part in memory, etc
As for where you are, the line table already will tell you that the column number changed on pc address advance, but line number did not. Thus, you know that you moved an expression, but not a line.
GDB doesn't happen to support this, and simply looks steps until line number change.
But it's not fundamentally hard from the debugger perspective.
On the producer side: When it comes to knowing where you are, you know you can't produce a 1-1 mapping, so you don't try. You can of course, properly present inlined functions as if they were function calls, and gdb will even do this. But there are times when lines or expressions were merged, and there simply is no right answer.
Stack traces, however, are still line based and thus if an exception occurs (like null reference) you only get the line, not the statement.
Here's an example of gcc versus clang (a "frontend" for LLVM):
$ gcc-4.2 -fsyntax-only -Wformat format-strings.c
format-strings.c:91: warning: too few arguments for format
$ clang -fsyntax-only format-strings.c
format-strings.c:91:13: warning: '.*' specified field precision is missing a matching 'int' argument
printf("%.*d");
^
LLVM is does some cool stuff :). Some other nice examples are at http://clang.llvm.org/diagnostics.html.All of the debuggers I've seen are line based with the option of single stepping through assembly.
Hopefully the LLDB people will be able to modernize debugging to the degree that the clang guys have modernized error messages, but that still remains to be seen.
Anyway, those are compiler warnings rather than after-the-fact debugging. I don't think LLVM's debugger, lldb, does any better in this case.
That said, a huge cascade of them is something I try to avoid. Two or three, okay. Nine or ten, break it up and make it clear and readily debuggable.
Ignoring the "no space after if/while/for" issue, I'd suspect that code to not have been the author's intent if I just came across it.
if((b != nil)
&&(b->qid.type==a->qid.type)
&&(b->qid.path==a->qid.path)
&&(b->qid.vers==a->qid.vers)
&&(b->dev==a->dev)
&&(b->type==a->type)){
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
}
It keeps almost the same visual look and it uses the common convetion, except for the && at the beginning of each line. if(
b != nil &&
b->qid.type == a->qid.type &&
b->qid.path == a->qid.path &&
b->qid.vers == a->qid.vers &&
b->dev == a->dev &&
b->type == a->type
) {
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
} if(
b != nil &&
b->qid.type == a->qid.type &&
b->qid.path == a->qid.path &&
b->qid.vers == a->qid.vers &&
b->dev == a->dev &&
b->type == a->type
) {
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
}
(Lined up the "a"s to make it obvious that they're all the same.) int samedirfile( Dir *a, Dir *b )
{
if( a == b )
return 1;
return ( a && b ) &&
( a->qid.type == b->qid.type ) &&
( a->qid.path == b->qid.path ) &&
( a->qid.vers == b->qid.vers ) &&
( a->dev == b->dev ) &&
( a->type == b->type );
}
...
if( samedirfile( a, b ) ) {
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
} int samedirfile(Dir *a, Dir *b) {
if(a == b) {
return 1;
}
return (a && b)
&& (a->qid.type == b->qid.type)
&& (a->qid.path == b->qid.path)
&& (a->qid.vers == b->qid.vers)
&& (a->dev == b->dev)
&& (a->type == b->type);
}
...
if(samedirfile(a, b)) {
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
}Deleted comment
if(b != nil){
if(b->qid.type==a->qid.type){
if(b->qid.path==a->qid.path){
if(b->qid.vers==a->qid.vers){
if(b->dev==a->dev){
if(b->type==a->type){
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
}}}}}}
I will never understand the allergy to multiple braces on one line. if( /* b is same as a */
(b != nil)
&& (b->qid.type == a->qid.type)
&& (b->qid.path == a->qid.path)
&& (b->qid.vers == a->qid.vers)
&& (b->dev == a->dev)
&& (b->type == a->type)
){
fprint(2, "cp: %s and %s are the same file\n", an, bn);
ret = 1;
}Add seven to three then multiply the whole thing by twelve.
(7 + 3) * 12
if (null != foo);
bar();
This is valid code in C and Java, and bar() will always run in this case.Having seen people waste hours on such a semicolon, I always use braces, even in one-liners, because I never know when someone is going to break it out into multiple lines later:
if (null != foo) { bar(); }It does however indent the line after the semicolon to the same level as the if, which is at least a red flag that something is up, if you are used to how the auto-indenting normally works.
When I've seen this happen, it wasn't because of a semicolon added when the code was written. It was someone accidentally adding a semicolon to a line later, without realizing it. Unless they then went to the next line and hit the "fix indentation" key, they didn't catch it.
if (null != foo);
{
bar();
}http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS
since your eyes would flag:
if (null != foo); {
bar();
}
as badness.[edit: correct } to {, ta]
Because I've gotten used to Go's support for short assignment in an if, the semi doesn't look completely out of place there (though the construction here is not valid Go either).
if (null != foo) bar();
or if (null != foo) {
bar();
}More seriously, after more than 10 years of writing C, C++ and Java, I've never run into this problem. I still wrap mine out of habit (to prevent this dreaded occurrence), but I think good testing would obviate any need for this.
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
do_something(x, y);
} using (var outFile = new StreamReader(outputFile.OpenRead()))
using (var expFile = new StreamReader(expectedFile.OpenRead()))
{
///...
}
[1]: http://stackoverflow.com/a/1329765/458193 bool exists = b != nil;
bool sameType = b->qid.type == a->qid.type;
bool samePath = b->qid.path == a->qid.path;
...
if(exists && sameType && samePath) {
}
It's also a lot easier to inspect in a debugger, as all the conditions appear in the locals list. speedIsSlow = dict(downstreamSlow = downstreamSpeed < 200,
upstreamSlow = upstreamSpeed < 100)
if any(speedIsSlow.values()):
connectionIsDead = getPingTime() > 100
if connectionIsDead:
print("It's dead, Jim") if(b->qid.path==a->qid.path)
is interesting. This is not how you compare strings in C. So it is either a bug or dirstat() needs to guarantee that different results pointing to the same file always share the path string. Qid is a structure containing path and vers fields:
path is guaranteed to be unique among all path names cur-
rently on the file server, and vers changes each time the
file is modified. The path is a long long (64 bits, vlong)
and the vers is an unsigned long (32 bits, ulong). Thus, if
two files have the same type, dev, and qid they are the same
file.
http://man.cat-v.org/plan_9/2/stat for (int x = 0; x < width; x++)
for (int y = 0; y < width; y++)
if ((x + y) % 3) {
// ...
}
The semantics are kind of like using a comprehension.e.g. instead of:
for(int i=0;i<N;i++){
for(int j=0;j<K;j++){
for(int k=0;k<L;k++){
...
it might become i=j=k=0;
while(all_frob_indices(&i,&j,&k)){
....
with all_frob_indices() doing the i++; if(i>N){ i=0; j++ }...This makes code look more similar to e.g. itertools-constructs in python where you can easily make products, zips, ... from iterables.
Not necessarily a bad practice, but something to be aware of.
The only upside is that it satisfies someone's indentaphobia. No, thank you.
for(int i=0;i<N;i++){
}
correctly steps through `N` `i`-values, a version with your `all_frob_indices` would step through `N + 1` `i`-values (because your termination check is `i > N`). Of course, it's easy to fix this once it's spotted, but you won't spot it with the same ease that you'd spot an off-by-one error in a `for` loop, because your eyes (or at least my eyes) don't know how an `all_frob_indices` function should look as well as they do how a `for` loop should look.When constructing UI elements in code, I used to scope copy/pasted blocks: { Button button = xxx; // and some more }
{ Button button = xxx; // and some more but different }
In the rubydays it'd be: Button.new do |b| something end
Dir *b;
, and print the message, this condition would get a lot simpler. I'd find it more readable to write a function bool samefile(Dir *a, Dir *b)
, have it consist of a single-line return statement, and then have the caller just do if (samefile(a, b)) { ... }.Imagine for a moment that a stray ";" ends up at the end of one of those if statements. I don't care how, perhaps you just dropped your Warby Parkers on your keyboard or something. Not only is the code now broken, but it still compiles. With &&, it would fail to compile and the error would be caught immediately.
The moral of the story is that syntax is your friend, not your enemy. Use syntax as much as possible to catch errors. Especially with strictly-typed languages, you have an incredible tool for automated verification of certain portions of your program. Use it.
Imagine for a moment that a character ends up deleted at the end of one of those && expressions. I don't care how, perhaps you just dropped your Warby Parkers on your keyboard or something. Not only is the code now broken, but it still compiles. With if(...)s, it would fail to compile and the error would be caught immediately.
The moral of the story is that syntax is your friend, not your enemy. Use syntax as much as possible to catch errors. Especially with strictly-typed languages, you have an incredible tool for automated verification of certain portions of your program. Use it.
And also, your reply is technically incorrect, a typographical error or any kind is more likely to be caught in the && situation, and the style of reply makes you an insufferable geek.
I blogged about this more here: http://www.databasesandlife.com/do-not-use-automatic-code-re...
Also, your formatter looks pretty brain-dead. clang-format formats all of those cases (in C++) as in your green examples with Google style turned on. Even the Eclipse formatter should do a better job than what you've posted with the appropriate settings. When I advocate an auto-formatter, it doesn't make sense for you to respond with a blog post talking about a formatter seemingly designed to make everything worse.
So I can just run it over my code, no matter what editor I used and the code is fine. No arguing what is right, no thinking about where to put some stupid spaces or how to break a fucking line, just run gofmt, done.
"Also, your formatter looks pretty brain-dead", it is, alas, what Eclipse does, and that's pretty standard in the Java world, and it's also pretty standard to use its code formatter alas.
Once you have your two ifs at the same indent, the indenter can use that info to keep them at the same indentation level. It could even assume you mean (a&&b) vs (!a&&!b) and not the three-way (a&&b), (a&&!b), (!a) when you follow it with an else block (I think that would be the best heuristic)
Implementing this will get a bit hairy, but if you are writing an auto-indenter for C, you should be used to that.
So, all you would have to do is hit one extra backspace to signal your intent.
Alternatively, one could type
if(a) if(b) {
To signal that one wants if(a)
if(b) {
Same number of keystrokes, and, IMO, that space is slightly easier to type than the return it replaces.As an example:
case
when (a == 1)
dosomething
when (b == 2)
dosomethingelse
when (c)
orthis
else
awww
end And[
a,
b
]
You can also rely on the fact that logical expressions are always short-circuited, so False && Print["won't be printed"], doesn't print anything.Finally, you can put a bunch of conditions in a list and do And @@ list.
This is true for any language I can think of.
E.g. in perl
open(my $fh, "<", "input.txt")
or die "cannot open < input.txt: $!";
is idiomatic if(not A and B)
but I'm not sure I've ever seen anyone do it. http://en.cppreference.com/w/cpp/language/operator_alternati...To get them in C, see the note about iso646.h in the link.
Also, it's not really that hard to #define it yourself, instead of using includes.
Note that in C++ they are keywords which is superior to macros.
if (b != nil)
if (b->bla == a->bla)
is so much clearer (and language agnostic) than if (b != nil && b->bla == a->bla)If someone who worked with me wrote that I'd talk to them about it and make sure they never did anything like that ever again.
From the terrible argument names to the abuse of the single line if syntax (which should never be used anyway, always use curly braces.)
It is like football, it does not matter how brilliant a team plays, but which one has higher score when the referee signals the end of the game.
It is just that I personally never found Plan9 that interesting, except for its successor Inferno and Limbo.
For me operating systems that explore designs done with regards to micro architectures or safe systems programming languages are much more interesting.
So for me Plan9 tends to be just another OS. And yes I have used it.
Yes, of course, I know the argument: a one-line bracketless "if" can set-up a future developer for failure if they need to add an item to the conditional block. And if they for some reason decide not to read the actual conditional. And if they don't test it.
And I don't hate code that uses braces even when they're not strictly needed, but I personally prefer to omit them. And my feeling is that the dogma around braceless-ifs is a little overblown.
Of course when working on a team that has adopted a no-braceless-if policy, I conform. Having consistent code is way, way more important than somebodies own favorite bracing style.
But you're taking it to a level for snark that really isn't warranted. I can think of at least 4 ways to iterate a list. It's about doing something the easiest/best way for the circumstance.
How about this one? http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f...
There's worse songs than "Who Let the Dogs Out" but it's still a really bad song.
https://feralbynight.googlecode.com/files/FeralbyNightv3_2_b...
Honestly for the sake of being more robust, I'd add if(a != nil) after the first test of b.
if(b != nil) if(a != nil) if ...
http://plan9.bell-labs.com/sys/doc/compiler.html
You can nerdmuse yourself a little by googling why it had a
#pragma hjdicksbtw there are no tabs specifically because it's meant to be read as a single statement, not nested conditions.
if(foo == bar
&& baz == qux
&& ! frob
&& whatever()
&& something) { #if foo #if bar #send obj:dosomething if (one) {
if (two)
if (three)
if (soon) {
stuff
}} else {
else stuff
}
Personally, I think I won't have any trouble reading that, but I have noticed I'm somewhat more tolerant than others in this regard. Though I am more OCD than others in other ways. ret = 0; //y()
if (A)
if (B)
ret = 1; //x()
If the else statement is more involved, it gets difficult.