• Anon (unregistered)

    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...

  • Sole Purpose of Visit (unregistered)

    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?

  • S.Uspect (unregistered)

    And who makes sure that this jewel is executed every millisecond? A system clock with milliseconds precision?

  • LCrawford (unregistered)

    What? No check for hour rollover past 23? This post published at 32767:30:00.

  • Sole Purpose of Visit (unregistered)

    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.

  • bvs23bkv33 (unregistered) in reply to LCrawford

    your post is coming in three years

  • Block (unregistered)

    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.

  • AP (unregistered)

    "it's role" - standards are dropping!

  • I'm not a robot (unregistered) in reply to LCrawford

    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.

  • Smash (unregistered)

    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.

  • Smash (unregistered) in reply to AP
    "it's role" - standards are dropping!

    Hahaha, good one. You're funny.

  • RLB (unregistered)

    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).

  • niol (unregistered)

    Is it really C++ or Lisp?

  • Mr Bits (unregistered) in reply to Block

    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.

  • Harrow (unregistered)

    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.

  • Sleep (unregistered) in reply to S.Uspect

    Clearly the code then calls some function to sleep for one millisecond

  • urkerab (nodebb) in reply to Sole Purpose of Visit

    anybody who uses ternaries in PHP should be weaned off the habit

    FTFY (unless my Markdown powers fail, which is quite likely)

  • T.T.O. (unregistered) in reply to Smash
    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.
    Even better is the fact that in order to stuff these assignments inside ternaries, they are evaluated in && and negated in order not to short-circuit it. I wonder what would that programmer do if they were assigning a value from some variable instead of 0. ((mil = some_var) || true) && (...and so on...)?
  • Quite (unregistered)

    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.

  • dkf (nodebb) in reply to Mr Bits

    Indeed, this code could be running in an interrupt handler.

    Doesn't make it any prettier though.

  • Uhm (unregistered)

    TRWTF is that they didn't use the comma operator "(a=0, cond)" instead of the "!(a=0)&&cond" construction

  • Tekay37 (unregistered) in reply to Quite

    Yes, hopefully. If manual incrementation of time is absolutely neccessary, it should me as easy to read and understand as timestamp++;.

  • qwerty (unregistered)

    TRWTF Is even if they don't know the comma operator they could have used (a=0)||cond

  • Kith (unregistered)

    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.

  • HUH? (unregistered)

    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

  • Sole Purpose of Visit (unregistered) in reply to HUH?

    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.

  • Sole Purpose of Visit (unregistered) in reply to Uhm

    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.

  • Anonymous coward (unregistered) in reply to HUH?

    The latter results the creation of a temporary, the former does not

  • JAB (unregistered) in reply to HUH?

    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.

  • gnasher729 (unregistered)

    Single expression and readable:

    (++mil == 1000) && (mil = 0, ++sec == 60) && (sec = 0, ++min == 60) && (min = 0, ++hou);

    Count me not impressed.

  • NULL (unregistered)

    It's like watching a house burn down, horrible of course, and yet oddly compelling and beautiful

  • spadgas (unregistered)

    Isn't that ub? multiple assignments to a variable without a sequence point?

  • Simon Wright (github)

    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%.

  • herby (nodebb)

    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.

  • QuiteSatisfied (unregistered) in reply to Quite

    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.

  • gleemonk (nodebb)

    I don't know in what tortured mind "!(mil = 0) && ..." could be considered elegant.

    Good WTF, would WTF again.

  • JBanana (unregistered)

    Bug report: stopwatch slightly out after a leap second

  • Chris (unregistered)

    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.

  • Nupanick (unregistered)

    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..."

  • anonymous (unregistered) in reply to Quite

    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.

  • SD (unregistered) in reply to herby

    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".

  • markm (unregistered)

    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.

Leave a comment on “As the Clock Terns”

Log In or post as a guest

Replying to comment #:

« Return to Article