1. 10
    #define macro(x) do { foo(x); bar(x); } while(0)
    

    The example macro, as it stands, evaluates x twice, which is a macro faux pas.

    Also, it is ill-advised to use a macro as a statement within another statement aside from a compound statement, regardless of the do {...} while (0) convention. It very likely will mislead the reader into thinking it’s actually an expression. This is a matter of style for the most part, but generally a good one, in my experience.

    1. 1

      very likely will mislead the reader into thinking it’s actually an expression

      Why? Do you never call void functions?

      1. 3

        Sure I do. But then, if you use this macro in an if condition, you’ll be a bit surprised.

        1. 2

          No more so than if you used a function or variable returning void, or a structure. I don’t see as it’s particularly of concern.

          1. 5

            The point is to signal to the reader immediately that they are dealing with something that may only be well defined from a specific context that is beyond what one would expect from the language. Since macros can expand to pretty much anything, it’s helpful to the reader to know that what they are looking at may be very different than what it seems. That’s why the convention of uppercase naming for macros is useful. And also why it’s useful to put them in a context that doesn’t lead you to believe it might be a function and evaluated as such. (A statement that is just foo(); looks more like a function than anything else. But if you write it { foo(); } then it’s more likely to signal to the reader that there may be more going on than what appears to be a function call.)

            And of course that can be violated and of course you’ll find out one way or another. Like I said, it’s a matter of style. I think it’s poor style.

            It also doesn’t help that the original macro is an utterly uncompelling example. If you are writing a macro to do a common sequence of operations, you are better off to use a function (possibly inline) to avoid the problems of text replacement from macros in the first place.

            1. 1

              Yes, I completely agree with you. A function would suit this case better. However, that is not the point of the post.

              1. 2

                What you have is not wrong (although the multiple evaluation is questionable), but the motivation needs to be there for the post to have more impact. What your post describes is a well-known problem in C. The do while (0) approach has been around for a very long time. With the long history of C and the sheer number of lines of code out there, it would probably be better if you found an actual example from real code to make the point.

                Here is one to consider. It’s in GCC and it even uses the the style that I disagree with, although in this case it’s only used in a single function so maybe there’s a better case for it. The history of it shows that it didn’t use the do while (0) pattern at first, which led to a dangling else issue. That has a lot more to it than a foo bar cooked up example. It’s also instructive to show why it is not a function, even though it looks like it could be.

                I understand that you’re probably learning your way through C. That isn’t a problem. However, you may want to cite some actual code to show the interesting (or weird) parts of the language. Code search is actually a reasonable thing these days and there’s no shortage of C code to look at. Good luck!

    1. 13

      About 25 years ago, an interactive text editor could be designed with as little as 8,000 bytes of storage. (Modern program editors request 100 times that much!) An operating system had to manage with 8,000 bytes, and a compiler had to fit into 32 Kbytes, whereas their modern descendants require megabytes. Has all this inflated software become any faster? On the contrary. Were it not for a thousand times faster hardware, modern software would be utterly unusable.

      Alright; I’d like to see people using an editor fitting in 8k of storage compiled with a language fitting in 32k[1] as a daily driver.

      Okay, perhaps it’s a bit lame to focus on those numbers specifically; but my point is that programs have also become immensely more useful. Editors – even editors like Vim – today do a lot more than 40, 30, 20, or even 10 years ago. And how long did it take that guy to build that TODO app with his 13k dependencies? How long would it have taken 30 years ago to build a similar program?

      And look, I’m not a huge fan of large dependency trees either; but I think this article is a gross oversimplification.


      [1]: tcc is 275k on my system.

      1. 7

        Over time you should expect things to become more powerful and easier to use! Look at microcontrollers for instance. They have become smaller, faster and easier to use over time. The argument is that software seems to be getting slower at a more rapid rate than it is gaining functionality.

        Hardware has increased in speed and gained fancy new features at the same time. Why is it that modern websites are sluggish? Why can’t we have our cake and eat it too? More complicated software that is also faster (given that the hardware is getting faster – this should be free).

        I don’t think software being more powerful or complicated is at all a fair argument for why it is slower.

        1. 6

          I don’t think software being more powerful or complicated is at all a fair argument for why it is slower.

          I think it’s entirely fair. That doesn’t excuse sluggishness in modern software for some tasks (like say, typing lag), but it does explain some of it. If you’re doing a lot more work you should expect it use more resources. There is also user expectation: those with more computing resouces tend to want their software to do more.

          I don’t want my software to be sluggish either (it drives me up the wall when it’s slow for basic tasks, which is why I cannot abide web apps for the most part), but if you’re going to compare old software that did very little with new software that does a lot as the post does, then discounting the feature sets is not at all a fair comparison.

          1. 8

            I would 100% agree with you if the medium on which software is run on hadn’t increased in speed by orders of magnitude over the years. Has software increased by orders of magnitude in terms of power and complexity? Maybe, but then it should be the same speed as software was a few decades ago.

            The fact is that software has gotten more complex and more powerful, but not nearly to the extent that hardware has gotten faster. There is certainly some reason for this (although I disagree with the article on what that is).

            1. 4

              Certainly it seems like it should be a lot faster, and maybe it’s slow, but there’s no question that I’m more productive on systems now than I was in the mid 90s.

              1. 3

                Recently, I was on a Windows machine and was obligated to write a text file. It had NotePad++, which I remembered as a “good editor,” so I used it. On Linux/Mac I’ve gotten used to a well-tuned neovim running in a GPU accelerated console… suffice to say that Notepad++ is no longer in its previous category.

          2. 5

            Most webpages aren’t that slow; most of the “big” websites generally work quite well in my experience. There are exceptions, of course, but we tend to remember negative experiences (such as websites being slow) much more strongly than positive ones (websites working well).

            A lot of the slowness is from the network. When you’re sitting in front of a webpage waiting for it to load stuff then most of the time the problem is that the HTML loads foo.js which triggers a XHR for overview.json and the response callback for that finally triggers data_used_to_render_the_website.json. Add to that the 19 tracker and advertisement scripts, and well… here we are.

            There’s a reason it works like that too, because people want mobile apps (for different platforms!) and whatnot these days too, and turns out it’s actually quite tricky to serve both a decent website and a decent mobile app. It’s probably underestimated how much obile complicated web dev.

            Note that some things are ridiculously slow. I have no idea how Slack manages to introduce 200ms to over a second of input lag on their chat; it’s like using a slow/inconsistent ssh connection. It’s been consistent crap ever since I first used it 5 years ago and I don’t quite understand how people can use Slack daily without wanting to chuck their computers out the window. But slow and crappy software is not a new thing, and Slack seems the exception (for every time I visit Slack, I’ve also visited dozens of sites that work well).

            At any rate, it’s much more complex than “programmers stopped thinking about the quality of their programs”.

            1. 5

              Most webpages aren’t that slow; most of the “big” websites generally work quite well in my experience. There are exceptions, of course, but we tend to remember negative experiences (such as websites being slow) much more strongly than positive ones (websites working well).

              We certainly use a different subset of the modern web! I find even GMail is sluggish these days, and often switch to the HTML-only mode. Jira (and basically all Atlassian projects) are what I would call “big” websites and wow are they slow.

              A lot of the slowness is from the network. When you’re sitting in front of a webpage waiting for it to load stuff then most of the time the problem is that the HTML loads foo.js which triggers a XHR for overview.json and the response callback for that finally triggers data_used_to_render_the_website.json. Add to that the 19 tracker and advertisement scripts, and well… here we are.

              Eh, I don’t fully buy this. Is it the network’s fault that every website comes bundled with 30 JS modules that need to load and then call out for more crap? I mean sure, with no-js this doesn’t become as much of an issue – and I don’t actually understand how someone can use the modern web without it – but I wouldn’t blame the network for these problems.

              There’s a reason it works like that too, because people want mobile apps (for different platforms!) and whatnot these days too, and turns out it’s actually quite tricky to serve both a decent website and a decent mobile app. It’s probably underestimated how much obile complicated web dev.

              Modern webdev is unbelievably complicated. I’ve been working on a project recently that dives into the depths of linker details, and it is nothing compared to how complicated setting up something like webpack is. But I would also argue that this complexity is superficial. Things like Svelte and Solid come to mind for what I think the modern web should look more like.

              Note that some things are ridiculously slow. I have no idea how Slack manages to introduce 200ms to over a second of input lag on their chat; it’s like using a slow/inconsistent ssh connection. It’s been consistent crap ever since I first used it 5 years ago and I don’t quite understand how people can use Slack daily without wanting to chuck their computers out the window. But slow and crappy software is not a new thing, and Slack seems the exception (for every time I visit Slack, I’ve also visited dozens of sites that work well).

              I’m right there with you! Its really unfortunate that no matter what company I go to and how good their engineering fundamentals are, the tools used are Jira, Slack and every other slow website.

              At any rate, it’s much more complex than “programmers stopped thinking about the quality of their programs”.

              I completely agree with you! Unfortunately, I’ve seen quality take a back seat far too many times to “just get something to work!” that I do think it is a part of the problem.

              1. 5

                I’m not a huge fan of modern web dev either; in my own app I just use <script src=..> and for the most part ignore much of the ecosystem and other recent(-ish) developments. /r/webdev called me “like the anti-vaxx of web dev” for this, as I’m “not listening to the experts, just like the anti-vaxx people” 🤷‍♂️😂

                But at the same time the end-result is … kind of okay, performance-wise anyway. Most of my gripes tend to be UX issues.

                Is it the network’s fault that every website comes bundled with 30 JS modules that need to load and then call out for more crap? I mean sure, with no-js this doesn’t become as much of an issue – and I don’t actually understand how someone can use the modern web without it – but I wouldn’t blame the network for these problems.

                That’s kind of an unrelated issue; a lot of these SPA websites are built against a JSON API, so it needs to call that to get the data and it just takes time, especially if it’s a generic API rather than an API specifically designed for the app (meaning it will take 2 or more requests to get the data). Good examples of this are the Stripe or SendGrid interfaces which feel incredibly slow not so much because they got funky JS, but because you’re waiting on those API requests

                1. 4

                  I’m not a huge fan of modern web dev either; in my own app I just use and for the most part ignore much of the ecosystem and other recent(-ish) developments. /r/webdev called me “like the anti-vaxx of web dev” for this, as I’m “not listening to the experts, just like the anti-vaxx people” 🤷‍♂️😂

                  This is hilarious! I’m with you though.

                  That’s kind of an unrelated issue; a lot of these SPA websites are built against a JSON API, so it needs to call that to get the data and it just takes time, especially if it’s a generic API rather than an API specifically designed for the app (meaning it will take 2 or more requests to get the data). Good examples of this are the Stripe or SendGrid interfaces which feel incredibly slow not so much because they got funky JS, but because you’re waiting on those API requests

                  That’s a good point. It might not necessarily be slow frontend, but it is still slow engineering. One of the previous places I worked at I fixed up an endpoint which was just spewing data, and upon talking to the frontend engineers they were using maybe 10% of it. Made a pretty significant speed improvement by just not sending a ludicrous amount of unused data!

                  1. 2

                    Yeah, it’s inefficient engineering, but consider the requirements: you need to make some sort of interface which works in a web browser, an Android app, an iOS app, and you frequently want a customer-consumable API as well.

                    This is not easy to do; if you build a static “classic” template-driven web app it’s hard to add mobile support; so you have to build an API alongside that for the webapp for the mobile apps to consume, which is duplicate effort. You can trim the API to just what you need for this specific page, but then other API users who do need that data no longer have it.

                    For a comparatively simple site like Lobsters it’s fine to not do that since the desktop UI works reasonably well on mobile too, but as soon as things start getting more involved you really need a different UI for mobile, as it’s just a different kind of platform.

                    It’s a difficult problem to solve, and it was much easier 20 years ago because all computers were of the same type (a monitor with a keyboard and a mouse). People are kinda figuring this out how to best do this, and in the meanwhile we have to suffer Stripe’s UI making 15 API requests to load the dashboard.

                    GraphQL is intended to solve this problem, by the way, but that has its own set of problems.

                    1. 1

                      Oh its certainly not easy, but I also don’t think it is particularly difficult. The reality is that there needs to be separate APIs for each of the use-cases (app, webpage and customer-consumable), since they all have different upgrade cycles and usage-types. One of the problems I see often is everyone wanting there to be one API for everyone and that will never work efficiently.

                      Netflix has a nice blog post [1] about how they handle the number of APIs they have (Netflix has gaming consoles, smart TVs and a whole host of other platforms that their API supports and it isn’t one “mega” API for all of them). They essentially have a Proxy API on the server-side which bundles all of the microservice APIs into whatever API calls the various frontends need. That way backend engineers can keep publishing APIs as they see fit for their microservices, and frontend engineers can group together what they need into an efficient API for their platform. And note, I’m using “frontend” loosely since there are so many different platforms they support.

                      Of course whether this effort is necessary for a small shop is unclear but for a bigger place (like Stripe or SendGrid) it is frankly poor engineering to not be fast.

                      I was very excited about GraphQL for a little while, but you’re right it does come with its own set of problems. Its still yet to be seen whether it is actually worthwhile.

                      [1] https://netflixtechblog.com/embracing-the-differences-inside-the-netflix-api-redesign-15fd8b3dc49d

                      1. 3

                        Of course whether this effort is necessary for a small shop is unclear but for a bigger place (like Stripe or SendGrid) it is frankly poor engineering to not be fast.

                        Yeah, I don’t know. Netflix is perhaps a bit of an outlier; they also managed to make their extensive microservice platform work very well for them, whereas many other organisations’ experiences have been markedly less positive.

                        Perhaps a big part of the problem is that there’s no “obvious” way to do any of this. That Netflix approach looks great, but also a very non-trivial amount of bespoke engineering effort with quite a lot of operational overhead. It doesn’t look like something you can pick off from the shelf and have it “just work” for you, so companies tend to focus their engineers other efforts as they feel that gives them better ROI. That sounds kinda lazy, but these kind of projects can also fail, or worse, get implemented and then it turns out it doesn’t work all that well and then you’re stuck with it and it becomes a liability.

                        The issue with GraphQL is that it’s really hard to implement well on your backend. Basically, it allows your API consumers to construct arbitrary queries against your database (okay, you can lock this down, but that’s how it’s supposed to be used). It’s certainly possible to facilitate this, but it’s not an easy problem to solve. Perhaps radical improvements in the network stack (e.g. with QUIC and others) will alleviate much of this (because all of that is also an inefficient organically grown mess).

                        On HN one of the Stripe devs said they’re working on fixing the performance of the dashboard a few weeks ago after I complained about it by the way, so at least they’ve acknowledged it’s an issue and are actively working on a fix. Perhaps things will get better :-) Actually, I already have the impression it’s better compared to half a year ago.

                        Also, I’d like websites from “small shops” to be fast as well. All of this shouldn’t be higher engineering you can only do if you can afford an ops team and 16 devs.

                        My own approach is to just use server-rendered templates with about 700 lines of jQuery sprinkled on top :-) It sounds very old-fashioned, but it actually gives very good results, IMHO. Then again, I’m not Stripe or SendGrid and am operating on a rather different scale in almost every way.


                        Also, related: last night (after my previous comment) I happened to be talking to a friend about the new reddit design, and I figured I’d try it and see if it’s any better (I normally use old.reddit.com). I couldn’t believe how slow it is. Like, I literally can’t scroll: it just jumps all the time and Chrome uses both CPU cores to the max. Granted, I have a pretty slow laptop, but what the hell? What a train wreck. My laptop can play movies, play games, compile code, and do all sorts of things, but it struggles with … a text website.

                        This is the kind of stuff you’re talking about. Yeah, some sites really are inexcusably bad.

                      2. 1

                        if you build a static “classic” template-driven web app it’s hard to add mobile support

                        It was like, a handful of lines of CSS and HTML for my static site to be supported on mobile with no frameworks. And often I go on a website and the sheer frameworks that they use get in the way of me using their site on mobile. This UK government coronavirus website was apparently optimized for mobile but none of the buttons worked, even when I zoomed in on them. This BBC News website was impossible to navigate because it kept on freezing up on my recent phone.

                        Whereas web forms from 20 years ago work fine and are perfectly fine to use, they might require some zooming but nowhere near as much effort to attempt to navigate, and at least they work.

                        Things will work well enough if you let go of the frameworks. Let the browser do it’s job!

              2. 1

                Hardware has increased in speed and gained fancy new features at the same time.

                Yet hardware also has loads of legacy cruft and compatibility layers making things slower than they would need to be. But that’s inevitable with the incremental nature of development. But I think incremental development is the only way to gain experience. And then every now and then you have a new technology come around and get a chance to start from a somewhat empty slate and apply what you’ve learned. I have high hopes for RISC V for example. And I think there are similar developments in software. Better tools and languages allow for writing better and faster software. A good example ist Rust, where you regularly see blog posts of Rust reimplementations of the supposedly pristine C implementations of ancient Unix tools outperform the C version, because the compiler can optimize more, because it has stronger guarantees from the language and you can use more high level constructs. Similar to that, I think that webassembly will improve the web performance story quite a bit.

              3. 5

                Having used some old editors that come with some C compilers prior to 1985 and running them on emulators to try and do real work on those systems, I can attest to them being woefully underpowered compared to modern editors.

                1. 2

                  I still have fond memories of the Lattice C (SAS/C) editor circa 1987. Maybe I’m a masochist.

                  That being said, I hate editors with a lot of features. I end up never using 90% of the features so all those features do for me is add complexity, slowness, bugs, and unexpected modalities. I suppose that explains why my favorite editors are ED (from AmigaDOS/TRIPOS), the original vi (yes I know modes), and sam…

                  1. 1

                    I used to be similar, but after neovim added async plug-ins (so the features don’t affect the performance of the main interface), I started to build a much more extensive collection of them. Language servers are a fantastic innovation, allowing the good parts of working in an IDE without loading up some monstrosity.

                2. 2

                  Tangentially,

                  I spent a few years using ex-vi (I’ve also in the past used ed(1) and vim’s vi(1)), C, POSIX, and surf(1), along with a minimal desktop environment (that was roughly, openbox with custom shortcuts, and XMonad) and it was pretty fun. It’s a nice feeling to know that my programs will work in a decade with minimal changes. Now I’ve moved to Python and Doom Emacs, on the one hand, nothing has changed much. Some things are maybe easier, maybe not. On the other hand, it’s given me a respect for the things that are easier.

                  One thing I will note is that the lag in using these new ‘high powered tools’ is much, much greater. Despite the fact that doom emacs, for example, goes out of it’s way to make latency when actually using the editor something that doesn’t bother the user. Loading up chromium takes an age when you’re used to surf popping up in under a second. Waiting for lightdm to start, log in, and then ‘start’ GDM is excruciating when you’re used to being dropped to a terminal after boot and loading Xorg in under a second.

                  There isn’t that much of an advantage to all of these bells and whistles, most of the complex stuff averages out because grep mostly gives results in the same time that an IDE takes to show the dialog. Everything you can do now you could achieve with shell scripts and get the same convenience and practically perform the task each day with about the same timing.

                  1. 1

                    Vim is 37MB. Can someone please explain to me why a text editor like vim needs such a huge size?

                    1. 5

                      Where do you get this number? The vim executable on my work Ubuntu desktop is 2.6 MB, another <megabyte of shared objects, and 4.5 MB for libpython3.6m which is optional. A download of Vim for Windows is 3.6 megabytes, so about the same. Did you miss a decimal point?

                      1. 2

                        Not to put words into @Bowero’s mouth but the latest VIM sources are almost 60MB, so maybe they were referring to source or source+buildtime resources or translations or something?

                      2. 5

                        37M seems wrong, it’s nowhere near that on my system:

                        -rwxr-xr-x 1 root root 2.6M Jun 12 18:05 /usr/local/bin/vim*
                        

                        That 37M probably includes runtime files? Which aren’t really needed to run Vim but are just useful:

                        88K     ./plugin
                        92K     ./colors
                        128K    ./tools
                        136K    ./print
                        140K    ./macros
                        224K    ./pack
                        276K    ./compiler
                        892K    ./indent
                        1.1M    ./ftplugin
                        2.1M    ./autoload
                        2.5M    ./tutor
                        3.6M    ./spell
                        6.7M    ./syntax
                        8.2M    ./doc
                        27M     .
                        

                        Does Vim need support for 620 different filetypes, detailed reference documentation, a “tutor” in 72 different languages, or some helpful plugins shipped by default? I guess not; but it doesn’t get loaded if you don’t want to use it. It’s just useful.

                      3. 1

                        I used an editor under MS-DOS that was 3K in size, and could handle text files up to 64k (if I recall—it’s been 30 years since I last used it, and I only know the size because I typed in the source code from a magazine). It was a full screen editor.

                        Turbo Pascal pre-4 was I think 50k, and that included the editor, so that would fit your criteria. Do these editors give the functionality found in modern editors or IDEs? No. But they are usable in a sense [1].

                        [1] In the “Unix is my IDE” sense, using the command line tools to search and process text.

                        1. 6

                          But would you still choose to use those tools today? I mean, Unix was built with teletypes and printers, so you can undoubtedly build very useful things with limited tools, but why use a limited tool when you’ve got enough computing power to use a more powerful one?

                          1. 1

                            I might, especially if I found myself back on MS-DOS. I’ve tried various IDEs over the years (starting with Turbo Pascal 3) and I never found one that I liked. Back in the 80s, the notion that I would be forced to learn a new editor (when I had one I knew already) is what turned me off. Since the 90s, I’ve yet to find one that wouldn’t crash on me. The last time I tried ( just a few years ago) I tried loading (never mind editing or compiling) a single C file (57,892 bytes) and it crashed. Hell, my preferred MS-DOS editor I used (40K executable, not the 3K one I mentioned above) written in 1982 could handle that file. You can’t use what doesn’t run.

                        2. 1

                          A few minutes?

                          #!/bin/sh -e
                          
                          mkdir -p "$HOME/.config"
                          
                          case "$1" in
                          ('')
                                  exec sed '=; s/^/  / p; s/.*//' "$HOME/.config/todo"
                                  ;;
                          (add)
                                  shift
                                  exec echo "$*" >>$HOME/.config/todo
                                  ;;
                          (del)
                                  tmp=$(mktemp)
                                  sed "$2 d" "$HOME/.config/todo" >$tmp
                                  exec mv "$tmp" "$HOME/.config/todo"
                                  ;;
                          (edit)
                                  exec $EDITOR "$HOME/.config/todo"
                                  ;;
                          (*)
                                  echo >&2 "usage: todo add text of task to add"
                                  echo >&2 "       todo del num"
                                  echo >&2 "       todo edit"
                                  ;;
                          esac
                          
                          1. 4

                            Right-o, now ask your mother, spouse, brother, or other non-technical person to use that. It’s equivalent (probably even better) for people like us, but it’s not really an equivalent user-friendly GUI program.

                            1. 1

                              I honestly think it could have been done likewise in 1985 (35 years ago) before people unable to use a shell would be entirely unable to access a computer’s files and features.

                              If it really got extremely popular, it could have been adopted by 30 users author included.

                              In 1995 whoever finding it on internet willing to give it a try would download it, spawn COMMAND.COM (Windows 95) try to run it, see it fail, open it and wonder “what the fuck?”, close it.

                              I guess a todo.apk would get some more luck today for roughly the same time in Android Studio.

                              1. 2

                                I honestly think it could have been done likewise in 1985 (35 years ago) before people unable to use a shell would be entirely unable to access a computer’s files and features.

                                Yeah, probably. But, for better or worse, the “average user” is quite different now than it was 35 years ago. Actually, this is probably one of the reasons things are so much more complex now: because while I’m perfectly happy with a script based on $EDITOR ~/.config/todo, this clearly won’t work for a lot of people (and that’s fine, not everyone needs to be deeply knowledgable about computers).

                                1. 1

                                  Agreed! A lot of things happen in 35 years! Software shaping society at high pace, society shaping software likewise.

                                2. 1

                                  Which means that the challenge is becoming increasingly harder for many factors (more, less skilled, more distributed people expecting more done faster by computers of more diverse types).

                                  We need keep the software stack manageable, as if a simple Todo app already takes us libraries to get the job done in reasonable time, I do not want to know what your accounting software will require to be maintained (SAP anyone?).

                                  And the TODO app made with 13k dependencies means a huge amount of time spent maintaining the 13k dependencies for everyone. Now we cannot stop maintaining these 13k dependencies because every TODO app in the world now relies on it.

                            2. 1

                              You can do a modern programming language with a text editor from 1994 : Rus Cox co-authored Go with ACME.

                              1. 2

                                Russ made a video intro on it: https://research.swtch.com/acme

                                1. 1

                                  You do not let any choice, I must try it again now! https://9fans.github.io/plan9port/ or http://drawterm.9front.org/ + http://9front.org/

                            1. 3

                              The arrow operator illusion does not work in ancient versions of C? Did C have significant whitespace?

                              1. 4

                                It seems like C has had significant whitespace, yes.

                                For the article, I have searched a lot to find out when this changed, but I was unable to find it. Because of your comment, I continued my search.

                                I’ve found an online version of the C89 standard, and in 2.1.1.2.7, I found this:

                                1. White-space characters separating tokens are no longer significant. Preprocessing tokens are converted into tokens. The resulting tokens are syntactically and semantically analyzed and translated.

                                This means that this comment has been in the C standard for over 30 years! And it still says no longer.

                                1. 14

                                  Section 2.1.1 is describing the eight translation phases. (A compiler is not actually required to make eight separate passes, but it does need to work the same as if it did.) What 2.1.1.2.7 is saying that white space stops being significant at the seventh phase.

                                  1. 11

                                    Oh god, that changes everything.

                                  2. 1

                                    Additionally, see The New C Standard: An Economic and Cultural Commentary, page 137:

                                    White-space characters separating tokens are no longer significant

                                    White-space is not part of the syntax of the C language. It is only significant in separating characters in the lexical grammar and in some contexts in the preprocessor. This statement could have equally occurred in translation phase 5

                                    So I think it’s highly unlikely whitespace was ever meaningful to C.

                                1. 3

                                  this part in the standard was directly taken from the C99 standard, so it is not that new. Sadly, older standards are no longer available on the internet.

                                  I think you meant C89 here? Because I found a copy of that quite easily which contains a similar statement: “7. White-space characters separating tokens are no longer significant. Preprocessing tokens are converted into tokens.

                                  Also, I think C89 was the first C standard? Before that whatever was in K&R book was the “standard”.

                                  1. 3

                                    Hi! Yes, I found it as well after the question by qznc. Not being able to find it while writing my article is sad, but it is even worse that I’ve used this version of the C89 standard before.

                                    Also, you are right about C89 being the first standard indeed. However, between C89 and C99, there have been two more versions.

                                  1. 5

                                    If I middle click the link, it goes to the correct place.

                                    1. 3

                                      Yes, opening in a new tab does not trigger the onClick event.

                                    1. 10

                                      I found myself telling people about floating point a lot. Now I try to do it in a more “show, don’t tell” way.

                                      This interactive style feels great. It really makes use of the fact that browsers can contain code to make it interactive. Maybe I should do more of that.

                                      If you have Javascript disabled, the evaluation will not work. Any suggestions how to mitigate that?

                                      1. 3

                                        If you have JavaScript disabled, the evaluation will not work. Any suggestions how to mitigate that?

                                        For those users, you can do what the excellent free online textbook Introduction to Computer Science using Java does in each chapter. Divide the article into multiple web pages with Back and Next links. At the bottom of each page, write a question, possibly with multiple parts. At the top of the next page, show the question along with the answer(s), and let the user manually compare their mental answer with the written answer.

                                        The textbook’s FAQ calls its style of writing “programmed learning” and describes it in more detail. The questions at the end of each page made the textbook much more interesting to read back in high school. Though your article’s style is slightly different, I found it more fun to read too because of its questions.

                                        The frequent-quizzing approach works better with smaller questions whose answers are easier to memorize. To aid the reader in memorizing their five boolean answers to each of your quizzes, you can put checkboxes next to each question. The checkboxes don’t have to do anything – their state will be saved in browser history, allowing users to go back and see their answers. As an example, chapter 63 of that textbook uses non-functional text fields in some of its short-answer questions.

                                        You could add server-side logic to cause each checkbox form to display different things on the next page, but for this particular article I actually think it would have been better if I could have clicked “show all answers” to see an explanation for every question, rather than clicking “evaluate” to see explanations next to only the questions I got wrong. It makes me more confident in my answers when I see that I got the answer for the same reason as the author. With the current behavior, it could be that I got the right answer for the wrong reason.

                                        1. 5

                                          No need to create multiple pages, you can just use the <details> HTML5 element.

                                          1. 3

                                            That’s a good place to start. If you (like me) find it to be just… too damned ugly (you can’t style it), you can use the checkbox hack to get the same visual behavior from arbitrary HTML elements.

                                        2. 2

                                          The interactive part really adds a lot to the article. Does anyone know whether Wordpress has a plugin to create things like that?

                                          1. 2

                                            If you have Javascript disabled, the evaluation will not work. Any suggestions how to mitigate that?

                                            If it is you who wrote that site, you could use plain old cgi to display the correct answer without javascript

                                            1. 2

                                              For hiding/unhiding elements without JS, I’ve always used <label>s to <input type="checkbox">. When the user clicks on the label, they toggle the checkbox. The label can be styled any way you want, though I often add cursor: pointer.

                                              The style implication is this. If you place the checkbox before the hidden element, you can hide both the checkbox and any hidden element and use input[type="checkbox"]:checked + #myhiddenelement {display: block} (or similar) to show the hidden element when the preceding checkbox is checked (i.e. when the user clicks the label).

                                              In addition, since most browsers preserve checkbox state across reloads and page navigation, you get to have the state of checkboxes saved, for free!

                                              1. 3

                                                Yep, this is great (though less awesome for screen readers).

                                            1. 1

                                              Hey Remy! Do you also use these digraphs, or only the word equivalents?

                                              <%          {
                                              %>          }
                                              <:          [
                                              :>          ]
                                              %:          #
                                              %:%:        ##
                                              
                                              1. 2

                                                No just the words. Actually only and and or. Otherwise the number of wtfs would rise quickly during code reviews…

                                                1. 3

                                                  You’re not the only one. Perhaps lobste.rs could use a function which allows users to vote for a new title?

                                                  1. 1

                                                    Small stuff like years are usually added automatically. This big of an edit usually requires a mod.

                                                    1. 4

                                                      Or a sufficiently consistent hivemind…

                                                1. 4

                                                  What is the purpose of this post, other than simply pasting the original header file? Even the comments were already in the original header file. Why wouldn’t you simply link to the original header file, if there is nothing to be added?

                                                  For those who can read C, the original git commit (which is actually extremely simple): https://github.com/git/git/tree/e83c5163316f89bfbde7d9ab23ca2e25604af290

                                                  1. 6

                                                    The purpose of the post is to get people curious about looking under the hood at Git’s code (which was very interesting to me). It is also to help clarify an aspect of how Git’s code works - the header file. The original header file does have comments, but they tend to be on the technical side. I expanded on those to help (esp newer folks) better understand how it works and to provide some context for the structures that are created.

                                                    As you mention, another approach would have been to write out my comments as points in the article itself and link back to the original version. But it helped me learn to go through and document the code in this way (esp in the context of the rest of the codebase), so I decided to make that available.

                                                    1. 5

                                                      My apologies, I was too quick with my judgement. You actually did add comments in the source code. Well done, they felt like they were there already.

                                                      1. 2

                                                        No problem, and thank you :)

                                                  1. 3

                                                    C++ digraphs are fun as well. Have had a few colleagues have a small heart attack when they read:

                                                    If (myfunc() or myotherfunc()) {
                                                    

                                                    Which is valid, just as “and” instead of &&, or bitor/bitand.

                                                    Trigraphs are removed as or c++17.

                                                    Source: https://en.cppreference.com/w/cpp/language/operator_alternative

                                                    1. 2

                                                      Does C++ just implicitly include a header like iso646.h, or are these real, independent operators?

                                                      1. 1

                                                        I feel like these are a lot more used than they should be. Many programmers actually prefer or over ||.

                                                        1. 2

                                                          I must admit I’m one of those. Even though I know the meaning perfectly, it feels more naturally to have or instead of ||. Not to mention it’s easier to type.

                                                          1. 1

                                                            I understand that very well and I think it is not that weird. All modern language support this already. It only is a problem in older systems. Sadly, I work on older systems a lot, so I have to be cautious with modern techniques while developing.

                                                            1. 3

                                                              Our codebase is migrating to c++11, but most of our programmers aren’t yet. If your still looking for an internship I could ask around at $job.

                                                              1. 1

                                                                People like you are the heroes no one deserves! Thank you so much for helping out. I have already been offered two amazing internships that I prefer however. :)

                                                                1. 2

                                                                  No problem, I saw it in your LinkedIn, and you’re near. If you’d ever like to do c++ development on coffee machines just message me,

                                                          2. 2

                                                            Many programmers actually prefer or over ||

                                                            Without actual numbers, I would disagree with this, since anecdotally I have very rarely seen this used over multiple projects at multiple companies. It probably varies by company and field.

                                                            I’m aware of it like a lot of other language features, but choose not to use it myself since it’s never been a convention anywhere I’ve worked and might lead to confusion. Mayhap this might be changing due to the rapid rise of Python instead of Java as the initial language many people learn, and that Python has or but not || and Java has || but not or.

                                                            One rationale against or at places I’ve worked which also used Ruby was that in Ruby, or actually has lesser precedence than || and can change the meaning of code in subtle, curious, and despicable ways.

                                                            1. 1

                                                              I think || is better than or because it keeps the symmetry with |. Same with && and &.

                                                          1. 2

                                                            Out of curiosity, has anyone ever actually used trigraphs in anger? Or maybe seen old code which did?

                                                            1. 2

                                                              I imagine that it can be done accidentally with cases like these, but I have never seen them in real, thankfully:

                                                              printf("What do you mean??!\n");

                                                            1. 5

                                                              Unfortunately that machine is too slow and has overheating issues, so I can’t run StepMania on it.

                                                              This must be the saddest thing that I’ve read today. Imagine modifying a kernel to play damn StepMania, only to discover this.

                                                              1. -1

                                                                love it, but no screenshots on main website and worst… no source code??

                                                                1. 4

                                                                  I do see screenshots on the homepage. Also, it is open source.

                                                                1. 13

                                                                  Fascinating, I never would have suspected that the backslash would take “precedence” over the single-line comment. The trigraphs are are an interesting historical factoid but I’d have been just as stumped by a comment ending with a regular backslash.

                                                                  1. 12

                                                                    If that’s really true, then this is the real “WAT?” in my opinion, and making this an article about trigraphs only clouds the message - I kinda didn’t catch the comment-extension issue, because all the time I was thinking only “meh, who sane would even enable trigraphs in the first place?” (Kinda similar level of a practical joke as #define while if in my opinion. I.e. “yes, sure, theoretically there could be some extreme reason to use it in some code; now please revert this change, take your chair to the corner of the room, and write 100 times in your notebook ‘I will never do this again’.”.)

                                                                    1. 8

                                                                      #define while if

                                                                      I would like to thank you for this advice.

                                                                      1. 5

                                                                        This is a also a lot of fun when someone has a Makefile with either a lot of CFLAGS or uses environmental variables for CFLAGS, because they a simple -Dwhile=if usually breaks everything.

                                                                        1. 1

                                                                          I like your style.

                                                                    2. 6

                                                                      Wikipedia mentions another interesting example:

                                                                      /??/
                                                                      * A comment *??/
                                                                      /
                                                                      

                                                                      This actually is a valid block comment.

                                                                      1. 5

                                                                        Yes, deleting backslashes before newlines happens very early in the compiler, even before preprocessing: http://port70.net/~nsz/c/c99/n1256.html#5.1.1.2

                                                                        The only thing that happens earlier is trigraph substitutions.

                                                                      1. 32

                                                                        If I were to compile a list of things I believe have the potential to make you a better coder, typing speed would very likely not even make it. Getting any sort of benefit from your amazingly high WPM output assumes you actually have something to type, which is – at least for me – most of the time not the case when programming. I spend much more time thinking about what to write and where to write it than I do actually typing. Does it then matter that I can, after an hour of investigation, write that 1 line fix in n seconds as opposed to n + 1?

                                                                        If you feel like your typing speed is holding you back, then by all means invest time in getting better at it. But if not, there are so many other areas you can focus on – like reading and understanding other people’s code – that have a much higher chance of making a better (and more efficient) coder out of you.

                                                                        1. 4

                                                                          I remember reading that 80 percent of our time on a project is reading instead of writing code.

                                                                          1. 6

                                                                            .. and thinking about the solution. that is also why i don’t get the “using the mouse makes me slow” argument. especially when some things are faster using the mouse, given the right tools :)

                                                                            1. 1

                                                                              When is using a mouse faster?

                                                                              1. 4

                                                                                The mouse is a very fast way to make a precise selection from fuzzy data.

                                                                                For instance, I’m often looking at outliers on a heatmap graph. Selecting a group of outliers to investigate with the keyboard would be slow and error-prone; mouse selection is near-instant.

                                                                                Over in radiology, a mouse is a popular way to adjust the windowing function used to render a scan (by adjusting the window size on one mouse axis, and the midpoint on the other). This would also be slower and more error-prone with a keyboard.

                                                                                1. 3

                                                                                  I find using mouse easier for switching between multiple files. Looping over dozens of windows is a nightmare with keyboard. Don’t suggest using a shortcut to focus window by its title, usually I don’t remember them.

                                                                                  Also, sometimes touchpad is faster, e.g. with Force Touch on MacBooks you can instantly look up the definition/translation of the word.

                                                                                  And so are touch screens. Using keyboard/mouse/touchpad is so inefficient after arranging windows with your fingers! Tiling window managers are not panacea.


                                                                                  And, ahem, how would you play DOOM on 9front without mouse?


                                                                                  1. 1

                                                                                    If I have too many windows open, I close some, or otherwise put them on different monitors or virtual desktops for instant navigation with shortcuts. Each to their own, of course. The main things I can’t not use a mouse for are FPS games and DAWs.

                                                                                    I first played the PSX version of DOOM, and a little later, a keyboard-only version. Vertical aiming isn’t necessary at all, and it’s perfectly serviceable without a mouse!

                                                                                  2. 2

                                                                                    for me:

                                                                                    • navigating in text is done faster using the mouse when jumping more than a few lines/words, even when displaying relative line numbers in vim.

                                                                                    • acmes mouse chording is really nice to use and required only a short time to become used to. i still wish copy/cut/paste would work like this everywhere. maybe modified a bit for the current mice, clicking with the wheel isn’t that great.

                                                                              2. 1

                                                                                If I were to compile a list of things I believe have the potential to make you a better musician, your ability at sight reading would very likely not even make it. Getting any sort of benefit from your amazing sight reading skills assumes you actually have something to play, which is—at least for me—most of the time not the case when playing music. I spend much more time thinking about what to play and when to play it than I do actually playing. Does it matter that I can, after a week of studying, play this piece 120bpm as opposed to merely 100bpm?

                                                                                If you feel that your musical reading ability is holding you back, then by all means invest time in learning solfège. But if not, there are so many other areas of musicianship you can focus on—like listening and understanding other people’s music—that have much higher chance of making a better (and more virtuosic) musician out of you.

                                                                              1. 6

                                                                                I have to pass -trigraphs to a modern version of gcc before this actually works.

                                                                                As soon as I read “trigraphs”, the “WTF” made perfect sense.

                                                                                1. 1

                                                                                  Got any bad experiences?

                                                                                  1. 5

                                                                                    It’s long been known that trigraghs are a disaster. No sane person would design that in today. It’s a vestige from days long gone kept around for compatibility reasons. The intense confusion they cause is why GCC disables them by default.

                                                                                    1. 5

                                                                                      If you don’t speak up for the trigraph users, will anyone speak up for you?

                                                                                      (The amusing parts is that EBCDIC does support these characters, but maybe that’s only on i, and z/OS continues to be a nightmare hell dimension…)

                                                                                  1. 2

                                                                                    !didIMakeAMistake() || CIsWrongHere();

                                                                                    If you understand how short-circuit evaluation works, you can understand that this will result in the following:

                                                                                    if (!didIMakeAMistake()) CIsWrongHere();

                                                                                    Wouldn’t that be if (didIMakeAMistake()) CIswrongHere(); or am I missing something?

                                                                                    1. 2

                                                                                      (edit to emphasize I support your statement, and you are correct)

                                                                                      In f(A) || g(B) g(B) is only evaluated if f(A) is false. If f(A) were true, there is no need to evaluate g(B) since the expression as a whole is going to be True no matter what. This is something to look out for when you have functions with side effects.

                                                                                      #include <iostream>
                                                                                      
                                                                                      
                                                                                      int side_effect = 0;
                                                                                      
                                                                                      
                                                                                      bool f(int x) {
                                                                                          return x > 2;
                                                                                      }
                                                                                      
                                                                                      
                                                                                      bool g(int x) {
                                                                                          side_effect = 1;
                                                                                          return x > 1;
                                                                                      }
                                                                                      
                                                                                      int main()
                                                                                      {
                                                                                          int x = 2;
                                                                                          if (f(x) || g(x)) {
                                                                                              std::cout << "Hello!" << std::endl;
                                                                                          }
                                                                                      
                                                                                          std::cout << side_effect << std::endl;
                                                                                      
                                                                                          return 0;
                                                                                      }
                                                                                      

                                                                                      You can change the value of x from 2 to 3 to see what happens. Try it out here: https://onlinegdb.com/SJMH13XRB

                                                                                      1. 1

                                                                                        Yes, you are completely right, thank you.

                                                                                      1. 2

                                                                                        How often do you ping websites to gather statistics?

                                                                                        1. 3

                                                                                          Every minute, it only does a head request to get the response code so it’s pretty lightweight.

                                                                                        1. 3

                                                                                          Great article! I noticed you mentioned that f(sleep(7)) is legal but not why. This is described in 6.5.2: http://port70.net/~nsz/c/c99/n1256.html#6.5.2.2p6

                                                                                          If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined.

                                                                                          Notice how it never mentions how to determine the number of parameters (clearly you can’t find it from the prototype). This is a concession to pre-C89 code, which did not have function prototypes at all! I actually tried to compile some of my professor’s code from 1989 which was using this feature a while back.

                                                                                          Some more discussion of this misfeature here: https://github.com/jyn514/rcc/issues/61

                                                                                          1. 2

                                                                                            Wow, what an amazing contribution, thank you!

                                                                                            Does an integer promotion then also mean that the argument is automatically executed?

                                                                                            1. 3

                                                                                              I am not sure what you mean by automatically executed. The arguments to a function are evaluated in an unspecified order before the function is called, but integer promotion means something different: types are promoted to int or unsigned int before being passed, as per 6.3.1.1 (http://port70.net/~nsz/c/c99/n1256.html#6.3.1.1p2):

                                                                                              If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions.

                                                                                              1. 2

                                                                                                The arguments to a function are evaluated in an unspecified order before the function is called, […]

                                                                                                I was actually referring to this when I said executed. Thank you!