6 ms·
> is this class of code that you have to run a single time to know it works Not sure what you're trying to say here.
by catskul2 3y ago
> is this class of code that you have to run a single time to know it works
Not sure what you're trying to say here.
- deleted 3y ago[deleted]
- jstimpfle 3y agoTake as an example printf("%d\n", foo->x);. Assuming it compiled but assuming no further context, what could break here at run-time? foo could be NULL. And the type of foo->x could be not an integer. Let's assume you run the code once and observe that it works. What can you conclude? 1) foo was not NULL at least one time. Unfortunately, we don't know about all the other times. 2) foo->x is indeed an integer and the printf() format is always going to be fine -- it matches the arguments correctly. It's a bit like a delayed type check. A lot of code is like that. Furthermore, a lot of that code -- if the structure is good -- will already have been tested after the program has been started up. Or it can be easily tested during development by running it just once. I'm not saying it's impossible to write a bad printf line and never test it, only to have it fail years later in production. It's absolutely possible and it has happened to me. Lessons learned. I'll even go as far as saying that it's easy to have errors slip on refactors if there aren't good tests in place. But people are writing untyped Python or Javascript programs, sometimes significant ones. Writing in those is like every line was a printf()! But many people will through great troubles to achieve an abstract goal of type safety, accepting pessimisations on other axes even when it is ultimately a bad tradeoff. People also like to bring up issues like this on HN like it's the end of the world, when it's not nearly as big of an issue most of the time. Another similar example like that are void pointers as callback context. It is possible to get it wrong, it absolutely happens. But from a pragmatic and ergonomic standpoint I still prefer them to e.g. abstract classes in a lot of cases due to being a good tradeoff when taking all axes into account.
- tialaramex 3y ago> I'm not saying it's impossible to write a bad printf line and never test it, only to have it fail years later in production. It's absolutely possible and it has happened to me. Lessons learned. A modern compile time type checked formatter would have prevented this mistake, you are deliberately choosing to use poor tools and calling this "pragmatism" because it sounds better than admitting you're bad at this and you don't even want to improve. In fact C++ even shipped a pair of functions here. There's a compile time type checked formatter std::format, which almost everybody should use almost always (and which is what std::println calls), and there's also a runtime type checked formatter std::vformat, for those few cases where you absolutely can't know the format string until the last moment. That is a hell of a thing, if you need one of those I have to admit nobody else has one today with equal ergonomics.
- jstimpfle 3y agoThanks for the ad hominem, but let's put that into perspective. My current project is a GUI prototype based on plain Win32/Direct3D/Direct2D/DirectWrite. It currently clocks in at just under 6 KLOC. These are all the format calls in there (used git grep): fatal_f("Failed to CreateBuffer(): %lx", err); fatal_f("Failed to Map() buffer"); fatal_f("Failed to compile shader!"); fatal_f("Failed to CreateBuffer(): %lx", err); fatal_f("Failed to create blend state"); fatal_f("OOM"); fatal_f("Failed to register Window class"); fatal_f("Failed to CreateWindow()"); fatal_f("%s failed: error code %lx", what, hr); msg_f("Shader compile error messages: %s", errors->GetBufferPointer()); msg_f("Failed to compile shader but there are no error messages. " msg_f("HELLO: %d times clicked", count); msg_f("Click %s", item->name.buf); msg_f("Init text controller %p", this); msg_f("DELETE"); msg_f("Refcount is now %d", m_refcount); msg_f("Refcount is now %d", m_refcount); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); fprintf(stderr, "FATAL ERROR: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); snprintf(utext, sizeof utext, "Hello %d", ui->update_count); snprintf(filepath, sizeof filepath, "%s%s", int r = vsnprintf(m_buffer, sizeof m_buffer, fmt, ap); int r = vsnprintf(text_store, sizeof text_store, fmt, ap); snprintf(svg_filepath, sizeof svg_filepath, "%s", filepath); That's theory and practice for you. The real world is a bit more nuanced. Meanwhile I have 100 other, more significant problems to worry about than printf type safety. For example, how to get rid of the RAII based refcounting that I introduced but it wasn't exactly an improvement to my architecture. But thanks for the suggestion to use std::format in that set of cases and std::vformat in these other situations. I'll put those on my stack of C++ features to work through when I have time for things like that. (Let's hope that when I get there, those aren't already superseded by something safer). (Update: uh, std::format returns std::string. Won't use.)
- spkm 3y agoMaybe if you used a typesafe language, you wouldn't have to track down 100 other more significant problems (:
- 3y ago