- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
I'd call this an abuse of the ternary operator, but it's not that bad, is it? I understood what it was doing on the first read-through, and I would expect most professional software developers to be capable of doing the same...
Admin
Well, it does the job. (Note that the use of assignment, rather than an equality test, is intentional.) I mean, there are clearer ways to do this, but it works.
Perhaps it was translated from a particularly bit-twiddling snippet of assembler?
Admin
And who makes sure that this jewel is executed every millisecond? A system clock with milliseconds precision?
Admin
What? No check for hour rollover past 23? This post published at 32767:30:00.
Admin
Unusually for a WTF, it also has the, ahem, "merit" of being portable across several language barriers, partly because it isn't really C++, it's C. All the target language needs to support is:
Ternaries
Pre-increment
Silent coercion of (int)0 to false
statements having the value of the resultant LHS
So, it doesn't quite work in C#. And PHP is also out, but arguably anybody who uses ternaries in PHP should be weaned off the habit. And then, gently but firmly, clubbed to death.
Admin
your post is coming in three years
Admin
Using my Code Detective Hat™, I'm thinking :
int numberOfMillisToFormat = now-then; int hou=0; int min=0; int sec=0; int mil=0; for(int =0; i<numberOfMillisToFormat;i++){ --code } printf("formatted length of time! %d:%d:%d:%d",hou,min,sec,mil);
also milliseconds in C++ is an impending WTF in and of itself.
Admin
"it's role" - standards are dropping!
Admin
That's fine if it's a stopwatch, as the article says, i.e. measuring an amount of time rather than a time of day. You could express long periods in days (if they ever occur), but IMO keeping it in hours is perfectly OK.
Admin
I love the fact that there are assignments masquerading as comparisons in the middle of those ternaries to fool any programmer who doesn't pay attention.
Admin
Hahaha, good one. You're funny.
Admin
Meh. I've seen worse. Much worse. At least, once you get it, you get it. It needs better formatting, that's 90% of the problem here (the other 50% being using a ternary where an if would've been fine).
Admin
Is it really C++ or Lisp?
Admin
Why do you think that code written in C++ can't handle milliseconds? I suspect you're thinking more about the operating environment and less about the language. One millisecond is an eon on (for example) a 120MHz ARM Cortex-M4 with a lightweight RTOS.
Admin
The author probably examined his compiler output and discovered that with this technique the code is one-fourth as large and three times faster than using nested 'if' statements. This would have been true with early 80's C on a 286.
Admin
Clearly the code then calls some function to sleep for one millisecond
Admin
FTFY (unless my Markdown powers fail, which is quite likely)
Admin
Admin
Am I the only one to take a look at this code and think it's actually quite satisfyingly elegant? No, it's not quite perfect, but the paradigm is sweet.
Admin
Indeed, this code could be running in an interrupt handler.
Doesn't make it any prettier though.
Admin
TRWTF is that they didn't use the comma operator "(a=0, cond)" instead of the "!(a=0)&&cond" construction
Admin
Yes, hopefully. If manual incrementation of time is absolutely neccessary, it should me as easy to read and understand as
timestamp++;
.Admin
TRWTF Is even if they don't know the comma operator they could have used (a=0)||cond
Admin
I mean, I feel like it would be easier to track in mils (or secs, if you prefer floats) and do the conversion only for display purposes.
Admin
My money is on it being an embedded system where t he lead software engineer is coding that way because 30 years ago the ugly doe resulted in a compiler optimization of some sort.
I think there is even a reason that: for (int i=0; i<10; ++i) { stuff } USED TO run faster than: for (int i=0; i<10; i++) { stuff }
It's probably some optimization that mattered when CPUs ran at 5 KHz
Admin
Insofar as that matters, or ever mattered, it's nothing to do with the CPU speed and everything to do with the memory architecture. (Assuming no pipe-lining or compiler optimization or such.) You don't save much by using fetch-and-increment versus fetch-increment-and-store, but it might matter in a tight loop. And under no circumstances are you going to lose anything. (Even if operator overloading is taken into account.)
Obviously this doesn't really apply here. But if you're looking at the difference between pre-increment and post-increment ... you're looking in the wrong place.
Admin
Here's the C++ thing on that. The comma operator doesn't enforce precedence -- it's up to the compiler. The logical operators enforce precedence. Use the logical operators.
This, incidentally, is the main reason why you cannot override the comma operator (amongst a handful of other operators) in C++.
I live to serve.
Admin
The latter results the creation of a temporary, the former does not
Admin
By my understanding, ++i can still be useful over i++ if i is a non-trivial iterator, though perhaps modern compilers are smart enough to notice that the previous-i temporary doesn't need to be created as it is immediately discarded.
Admin
Single expression and readable:
(++mil == 1000) && (mil = 0, ++sec == 60) && (sec = 0, ++min == 60) && (min = 0, ++hou);
Count me not impressed.
Admin
It's like watching a house burn down, horrible of course, and yet oddly compelling and beautiful
Admin
Isn't that ub? multiple assignments to a variable without a sequence point?
Admin
Expanding to sensible if/else statements produced exactly the same code size (GCC). On the other hand, writing in Ada reduced the size by 10%.
Admin
In general, one should just count something like time in a single unit. The display of said time is a matter for programming. Thankfully the Unix people understood this and based their clock in seconds from the epoch in GMT. The display of the clock was a user space problem.
That being said, on an 8 bit micro project I worked on, I kept the time in BCD hours, minutes, and seconds, then the day of the year (again in BCD), and the last digit of the year. A total of 5 bytes, but it did the job. I also had a clock chip that kept the time in BCD anyway so it was easier to do. If I had done something like Unix, it would have required division, which the 8 bit micro didn't have. Oh, well.
Admin
You're right, I love everything about this code. It's like poetry.
And, like poetry, I would never use it at my day job and I would hate to maintain this stuff.
Admin
I don't know in what tortured mind "!(mil = 0) && ..." could be considered elegant.
Good WTF, would WTF again.
Admin
Bug report: stopwatch slightly out after a leap second
Admin
It would be interesting to see if a modern compiler would compile this differently to the equivalent if-else statements, and if so, which would be more efficient. E.g. there's an assignment, then an unnecessary conditional on the newly assigned value (and then inverted).
If anyone did this at my work, it would be ripped out and replaced. If it does somehow shave off a few cycles, it isn't worth it considering the readability. Yes, a seasoned programmer can tell what's happening, once they notice those are assignment operations mixed in there. For this code to persist, it would have to be used in some seriously limited system where every byte and cycle is valuable.
Admin
After reading this, all I can think is it looks like I just read a brute-force solution to counting.
"Okay, we want to add 1 to this number. So we increment the ones digit. But wait! Edge case! If the last digit is 9, then we don't do that, we set it to 0 instead and increment the tens digit instead. But wait, another edge case! What if that digit is 9..."
Admin
It's elegant in the sense that it's a one-liner that handles incrementing and rolling over a timer value. So yes, I think it's elegant. But it's the kind of elegance that's nice to see in a coding challenge, not in production code.
Admin
Hurrah! The only sane comment. I used to be ternary king but this is an abomination. It's merely a convoluted way of disguising the age old "time" problem". ie: proper programmers don't mess with it.
For those not with the plot: when did julian time change to gregorian time? That's just a superset of timezones. Timezones change on a political whim. Store UTC. Even that is complex enough but "good enough for govt work".
Admin
This somehow reminds me of something I once programmed for time-keeping on a PIC16, in assembly. Except for "!(mil = 0) && (sec == 59", of course. That unnecessary comparison wastes two whole instructions on a PIC (a SKIP and a GOTO), and it will waste a Branch instruction on every other architecture I am familiar with. The comma operator would avoid this - and on a small PIC, a few instruction words saved is important! Since this code must be in an interrupt handler that fires every millisecond, fast code is even more important, especially in a rather slow 8-bitter. A good C compiler would translate the nested ternaries (with the comma operator) the same as the nested-if equivalent, but there may still be 8-bit architectures for which a good C compiler either doesn't exist or is expensive.
But the plan is the same: There's a hardware counter that counts clock cycles and triggers an interrupt once a millisecond. The interrupt handler maintains a chain of software counters, mil, sec, min, and hour. (But "hou"? Is this programmer under the impression that the names take up space in the program?) At each interrupt, the software checks the mil counter, and either increments it or rolls it over and adds one to the next counter. But to a programmer familiar with assembly code, it's missing an obvious optimization: Increment first (which loads the memory slot into a register, then check for rollover, rather than loading the counter twice. In C:
if(++mil == 1000) { mil = 0; if(++sec == 1000) { sec = 0; if(++min == 60) { ++hour; } } }
I'd trust most C compilers for small microcontrollers to optimize the two-byte arithmetic with "mil", but would not be surprised if they would not find an optimization that eliminated loading "sec" twice for "sec = 0, sec == 59".
Even better would be to set the counters to 1000 or 60 and count down to zero - then the decrement would leave the zero flag set at zero, saving a comparison at each level.