1. 14

    The 5th level of achievement as a programmer is architecting your code in such a way that errors are impossible to compile. In other words, use a language with a sophisticated type system (and other forms of static checking).

    This article was written in 2005, meaning it was somewhat before the current era of actually using the good ideas from academic programming language research, and enough time has passed since then that it shows. Few people talk about Hungarian notation anymore, because ML-style types allow programmers to accomplish a much more general and robust version of the same thing without needing a special variable-naming convention - and languages with these type systems are pretty easy to use in production these days.

    1. 15

      While having a more sophisticated type system is definitely useful, you can use older type systems to enforce this type safety as well. E.g.

       typedef struct {char* s} unsafe_string_t;
       typedef struct {char* s} safe_string_t;
       unsafe_string_t read_input(socket_t s);
       void write_output(safe_string_t s);
       safe_string_t encode(unsafe_string_t s);
      
       ...
       unsafe_string_t in = read_input(sock);
       write_output(in); // whoops! won't compile
       write_output(encode(in)); // will compile and is correct
       ...
      
      1. 7

        You don’t need ‘ML-style types’ for this. You need opaque typedefs, a simple enough feature that I think it could be proposed for and added to C itself without kicking up a fuss.

        1. 4

          I’m pretty sure opaque typedefs is literally what was meant by ML style types. I’m not sure how it would work in C since you still need to be able to add to indices even when they have a non basic integer type.

          1. 4

            There’s also tagged unions and generics.

            1. 1

              ML-style types would normally refer to algebraic data types with (at least) first order parametric polymorphism. That means you need tagged unions, first class functions and closures.

              I’m not sure how it would work in C since you still need to be able to add to indices even when they have a non basic integer type.

              I’d like to just be able to define a typedef where you have to explicitly cast to and from it in order to use it as its underlying type. It would be super extra double nice if you could somehow apply it to enums too.

            2. 2

              Opaque non pointer typedefs?

              1. 2

                As in being able to say opaque_typedef int foo; and then being unable to do

                int x = 1;
                foo f = x;
                

                You would need to explicitly cast to and from the typedef and the underlying type. Would be useful for enums too, although how exactly it would be worded is a little complicated as I think you’d want to allow the values of the enum to be used without casting them, even though they’re integers.

                enum option {
                    OPTION_0,
                    OPTION_1,
                    OPTION_2,
                    OPTION_3,
                };
                opaque_typedef enum option option_t;
                
                option_t opt_1 = OPTION_1; /* ought to be legal */
                option_t opt_2 = 2;        /* ought not to be legal */
                /* however OPTION_1 is just an integer constant in C I'm pretty sure */
                

                Perhaps in this wild fantasy world I’m suggesting the C standard could allow explicit underlying type annotation for enums and then allow you to use opaque_typedef there:

                opaque_typedef int option_t;
                enum option : option_t {
                    OPTION_0,
                    OPTION_1,
                    OPTION_2,
                    OPTION_3,
                };
                
                option_t opt_1 = OPTION_1; /* legal, OPTION_1 is of type option_t */
                option_t opt_2 = 2;        /* not legal, 2 is not of type option_t */
                

                but then what about flags where you want to be able to bitwise-or them without casting and set the values of the options explicitly, would you need to do enum : flags_t { ... FLAG_3 = (flags_t)4, FLAG_4 = (flags_t)8, ...}, etc.?

                Anyway I think the idea has merit and really just needs someone to write it up and submit it to the committee probably…

            3. 4

              As far as I know type systems can’t ensure that a program conforms to a specification and can’t check the soundness of that specification. In my experience, type systems are the way to go but in order to make all errors impossible to compile, formal methods are likely required.

              1. 2

                Or on the other side you can write Clojure where you name your variables with english words like list-of-names and capitalized-list-of-names that make them clear.

              1. 4

                We can all agree that it should. But they advertise that they don’t

                  1. 3

                    Works fine now.

                    1. 1

                      Maybe it’s blocked by geolocation? Your profile says you’re from Russia :/

                      1. 0

                        And so? Why Microsoft should block Russians?

                        1. 1

                          Not saying they should, just that they may have decided to.

                          1. 1

                            Still a weird first assumption. Also the post prior to yours was from a russian saying it works now.

                        1. 1

                          That’s weird. Works for me, both that website and the linked PDF.

                          1. 1

                            Works here too.

                          1. 2

                            The “this will give you more elements than you asked for” thing seems like a pretty big footgun

                            1. 2

                              I updated the article to clarify that it only realizes the lazy structure in chunks, and it returns a sequence with the correct number of items :)

                              1. 1

                                Does it know if the closure used to map is pure or not?

                                1. 2

                                  Nope, it has no idea. It will run the mapping function regardless. Clojure has no type system that blocks you from accidentally calling the network when you add two numbers :)

                            1. 10

                              Linus’s point that nobody checks errors for close is strong. For example, ignoring errors for close is an explicitly documented behavior of Rust standard library: https://doc.rust-lang.org/std/fs/struct.File.html

                              Files are automatically closed when they go out of scope. Errors detected on closing are ignored.

                              1. 2

                                I never handle errors on close() because usually I’m in the destructor of an object (in C++ with RAII) and there is just no way to handle it properly since you aren’t supposed to throw exceptions in destructors and the destructor cannot return a value. Also, how should I handle a close() error? Should I retry opening the file and redo all the operations? My program might have had this handle open for hours if not days, it cannot cache all the things it has done to the file. It is extremely unclear what could cause close() to return an error and how to handle it. If nobody knows how to handle an error, it is likely that everyone will ignore it. It is also unclear what breakage a close error can cause. Unless the file is of critical importance, I usually just log it and hope that someone reads the logs.

                                1. 2

                                  A decent pattern could be, in my opinion, using a temporary file as a bridge.

                                  Create a new file, fill it up with content. Try to close it: if everything worked, fsync worked, close worked, replace the old file with the new file by a rename.

                                  Upon failure you tell the user and don’t discard the old file. No data loss, just a lost update.

                                  Then I admit this approach does not scale well, if the file is huge. But again, every case is different.

                                  1. 1

                                    Also, how should I handle a close() error? Should I retry opening the file and redo all the operations?

                                    No, you report it to the user with the file path. At the bare minimum.

                                    Everything else depends on the error code returned and the semantics of your program.

                                    1. 1

                                      What would the user do about it?

                                      1. 1

                                        In the more general case of why should I tell my user about anything they cannot do anything about:

                                        • so they can be aware of it.
                                        • so they can take steps to verify the results
                                        • so they can file a bug

                                        This doesn’t mean you have to pop an alert on the screen of the end user but somehow (log file, event log etc) letting someone know (user, admin) is often a good idea.

                                      2. 1

                                        No, you report it to the user with the file path.

                                        The vast majority of software cannot directly interact with the user, and even if it could, it would be useless. Imagine you own an Epson printer and somehow the printer told you /var/epson/printer_conf.ini: error EIO on close().

                                        To the user, that is completely useless. Maybe you understand it because you are familiar with Linux systems, but imagine the average user seeing this message, they would be completely puzzled.

                                        Everything else depends on the error code returned and the semantics of your program.

                                        Okay, so the POSIX standard says close() can return EIO, EBADF and EINTR. How would you handle these errors in your software, other than logging it or reporting it to the user? Do you know any resources or existing code bases where developers can learn how to treat close() errors?

                                  1. 7

                                    Down under the level of assembly language is the level of hardware design diagrams. And under those are transistors. And down under that is the level of magnetic currents and fluctuations. And if you go down much further we don’t have language for the levels and the problems any more — my understanding bottoms out even before “quantum physics” — but even lower levels still exist.

                                    I like the article but this bugged me. It’s largely irrelevant what’s below that which can be achieved with assembly language, because that isn’t programming. You can’t change the hardware design with code (FPGAs aside…) so it’s not relevant to the discussion.

                                    1. 8

                                      It really depends on the context. If you’re a programmer for an embedded system, selecting an appropriate MCU for the task is arguably a fundamental responsibility of the job depending on how your company is organized.

                                      If you’re locked into the x86 world, you can report issues to AMD and Intel and it’s frequently possible for a fix to be delivered in a microcode patch.

                                      Edit: Also, to the fundamental argument of “hardware isn’t programming so it’s not important,” you should understand, at a minimum, one layer below the lowest layer that you work on. Otherwise, how will you know if your layer or the layer below it is misbehaving when you’re debugging a problem?

                                      1. 4

                                        I concur that understanding layer N-1 is a good idea. That, and having a deep understanding of what can go the application code. It’s very helpful to understand what sorts of “magic” can happen, and how to de-mystify them.

                                      2. 4

                                        I like the article but this bugged me. It’s largely irrelevant what’s below that which can be achieved with assembly language, because that isn’t programming. You can’t change the hardware design with code (FPGAs aside…) so it’s not relevant to the discussion.

                                        That can still be relevant: how CPU caches behave is super relevant for performance, but you can’t change how they behave. And even beyond that, Rowhammer showed us that even very low-level details like how closely chips are packed can be influenced all the way up from high level languages, even JavaScript. Even something as super-low-level like cosmic rays striking your RAM causing bits to flip might even be something that could crop up from time to time (and you could error-detect/correct it in software), especially if you’re writing code to run on spacecraft.

                                        1. 2

                                          Sure, but they’re not really relevant to the points that are “You should learn C”, “You should learn asm”, “You should write an OS” etc. There’s no “You should build an x86 machine from scratch”, because it’s not about programming, that’s electronic engineering. I thought about cache optimisation as part of that which can be achieved with assembly, which is relevant; hardware design diagrams are, for 99.99% of cases, not.

                                          1. 4

                                            Our main technical university teaches all those things. In my country you can’t become a (formally educated) web dev without learning about electronic engineering (it’s in the name even).

                                            1. 1

                                              It sounds like your country is doing it wrong.

                                              1. 1

                                                Sounds right to me

                                                1. 1

                                                  Well the school isn’t for web developers, it’s for engineers and computer scientists. However it just so happens that a lot of them become web developers because that’s relatively lucrative and a popular avenue at the moment.

                                                  The school does some things wrong but overall it’s a great, intensive program.

                                                2. 1

                                                  What university is that?

                                                  1. 2

                                                    https://www.fer.unizg.hr/en

                                                    I should mention I was wrong, there are some more vocational schools here where you indeed don’t learn about most of that stuff, they just teach you to code in something practical.

                                            2. 3

                                              Realistically, you can’t change the kernel or the web browser either.

                                              In a sense, it is useful to define software as something you can change, reclassifying the kernel and the web browser as part of hardware. Depending on your situation, Django or Rails also can be the hardware. While it is sometimes useful to know about the hardware, I agree it is mostly irrelevant for people working on software.

                                              1. 7

                                                You can realistically change the kernel and/or the browser, or more realistically write patches and improvements for the existing code. That’s not a realistic option with your processor.

                                                1. 0

                                                  No, you can’t. Maybe you are submitting your application to App Store, maybe your web app needs to run on iOS Safari. I guess you could apply to Apple, but then, you could also apply to Intel.

                                                  1. 7

                                                    Have you never compiled your own kernel or run Greasemonkey?

                                                    You very much can change both the browser and kernel. A locked-down platform means the platform is wrong, not the person attempting to modify it.

                                                    1. 1

                                                      If you feel like it, read iOS instead of the kernel and Safari instead of the web browser. My point still stands.

                                                      iOS and Safari are as unmodifiable as Intel CPU for me. That one is hardware and the other is software matters little.

                                                      1. 3

                                                        Using a CPU you can’t modify is unavoidable for anyone using a computer. Avoiding using iOS or Safari is very easy in comparison.

                                                        1. 10

                                                          You two are talking past each other: one of you is speaking corporate engineer at work and the other is speaking FOSS zealot. Both perspectives are valid, but neither is complete.

                                                    2. 3

                                                      All kinds of apps and updates get into the App Store. It’s a multi-billion dollar business generated by 3rd parties. Whereas, Intel’s silicon is totally made by them with no 3rd-party patches. Your comparison might work for microcode. They seem to make those changes on their own. Sparingly, too.

                                                      One can definitely get a valuable update into a web browser or onto Apple phone before making a change to pre-deployment or post-deployment silicon. Software shouldn’t be thought of like hardware unless it’s software nobody can change or understand. Closest comparisons are mission-critical, legacy systems in enterprises and governments.

                                                2. 2

                                                  But there are machines, like the PERQ where you can change the machine code.

                                                1. 1

                                                  Looks very similar to LabVIEW, which I am not a fan of. Modern method chaining is much easier to both write and read.

                                                  The interactive development does sound nice, though. Similar levels of interactivity are available in Jupiter notebook or React hot reloading, though. And as demonstrated by Casey Muratory in Handmade Hero, you can even get the immediate feedback of sub-second compile times and hot reloading in C/C++, if you architect your code correctly.

                                                  1. 1

                                                    What’s “modern” method chaining?

                                                    1. 1

                                                      I just meant to distinguish method chaining from old-school one statement per line. Method chaining provides a more visual conveyance of the data flow, IMHO.

                                                      1. 1

                                                        I just asked because cascading has been around since long before I was born.

                                                        I do agree that multiple messages per statement are great.

                                                        1. 1

                                                          True. Just not as much in mainstream languages until the last decade or two. And more recent languages reaching mainstream, e.g. Rust, are fully expression oriented.

                                                  1. 1

                                                    Can’t you just call malloc/free from Go?

                                                    1. 12

                                                      Glossing over why sudo would mysteriously not work, this writes a bunch of noise out to the monitor:

                                                      > sudo cat /dev/urandom > /dev/fb0
                                                      -bash: /dev/fb0: Permission denied
                                                      

                                                      That command does not work because the redirection is performed by the shell which does not have the permission to write to /dev/fb0. The redirection of the output is not performed by sudo.

                                                      How to do it here

                                                      1. 7

                                                        You can use tee as well:

                                                        cat /dev/urandom | sudo tee /dev/fb0
                                                        

                                                        It’s quite funny that person who don’t know how shell and sudo (nb: use doas or su instead) tells us a lengthy story about writing to framebuffers in Linux

                                                        1. 11

                                                          The frame buffer is arguably a lot simpler than a *nix shell

                                                          1. 7

                                                            nb: use […] su instead

                                                            You better don’t, it’s vulnerable to the ages-old TIOCSTI trick::

                                                            #include <sys/ioctl.h>
                                                            int main() {
                                                                char* c = "exit\nid\n";
                                                                for (; *c; ++c)
                                                                    ioctl(0,TIOCSTI,*c);
                                                                return 0;
                                                            }
                                                            

                                                            (in .bashrc or so)

                                                            This can be deterred by using su -P, but it’s marked as experimental (according to the manpage), and I haven’t seen anyone using it.

                                                            1. 3

                                                              TIOCSTI

                                                              more info here, as I haven’t heard of it previously https://ruderich.org/simon/notes/su-sudo-from-root-tty-hijacking

                                                          2. 1

                                                            This probably works for the author if they’re in the video group:

                                                            $ ll /dev/fb0
                                                            crw-rw---- 1 root video 29, 0  5 apr 15:37 /dev/fb0
                                                            

                                                            EDIT: I’m stupid, didn’t see that part of the article.

                                                          1. 16

                                                            Meh, what’s a factor 10 when you’re still under a request per second? Does this actually matter?

                                                            I’m curious whether any attempt was made to contact the Bing team before the change & post.

                                                            1. 3

                                                              Seems very unnecessarily frequent. If you had multiple search engines checking in this frequently it’d add up quickly

                                                            1. 1

                                                              On laptops, I use knuckles for modifier keys. On other keyboards, I just use my palm for the control key. Save caps lock for the escape key.

                                                              1. 1

                                                                I like having a tap on caps mapped to esc, and a press mapped to ctrl.

                                                                Actual ctrl key I map to ctrl+opt+cmd and press with the palm for custom combos.

                                                                1. 1

                                                                  That sounds interesting, how did you acomplished that?

                                                              1. 1

                                                                The main problem with front end development was never the browser, but rather the endless layers/kilobytes of abstraction meant to make everything automatic.

                                                                1. 1

                                                                  As soon as something other than a print statement is imported, you can be pretty sure that monads won’t be explained simply.

                                                                  1. 2

                                                                    A monad is a type m for which the following functions may be implemented:

                                                                    (>>=) :: forall a b. m a -> (a -> m b) -> m b
                                                                    return :: a -> m a
                                                                    

                                                                    Subject to the following rules:

                                                                    -- Rule 1
                                                                    return a >>= k  =  k a
                                                                    -- Rule 2
                                                                    m >>= return  =  m
                                                                    -- Rule 3
                                                                    m >>= (\x -> k x >>= h)  =  (m >>= k) >>= h
                                                                    

                                                                    That’s it. That is what a monad is (there are equivalent definitions that some people prefer over this one, but all of them give the same result). Monads get some special treatment in Haskell (do notation requires a monad), but other than that there’s nothing magical about them. Monads aren’t magical things. “Monad” is a name for a common pattern: a type whose definition permits the functions defined above.

                                                                  1. 18

                                                                    It’s been 9 hours and nobody has mentioned C++!

                                                                    I’m not suggesting you jump off the C++ cliff. Don’t use the STL. Don’t use RAII. Don’t follow “everything has value semantics and a = b does massive allocations and copies”. Don’t use the STL. Don’t use smart pointers. Don’t use exceptions - you can disable them at compile time. Maybe use the STL if you can’t be bothered to implement things yourself, but measure your build times before and after.

                                                                    Start with C, and introduce C++ features when they let you write the same C code but more concisely/safely. Use operator overloading for mathsy types. Add an array class that doesn’t own its data, just acts as a wrapper around T* and size_t (and give it bounds checking). Make a string class that wraps char buf[ N ]; and snprintf (_snprintf on Windows does not behave exactly like posix snprintf FYI). Use the SCOPE_EXIT trick for resource cleanup. Use casts that explode if you smash values by accident.

                                                                    Some examples of code I have that I think is neat and shouldn’t upset C programmers, starting with a better printf:

                                                                    stuct v3 { float x, y, z; };
                                                                    void format( FormatBuffer * fb, const v3 & v, const FormatOpts & opts ) {
                                                                            format( fb, "v3(" );
                                                                            format( fb, v.x, opts );
                                                                            format( fb, ", " );
                                                                            format( fb, v.y, opts );
                                                                            format( fb, ", " );
                                                                            format( fb, v.z, opts );
                                                                            format( fb, ")" );
                                                                    }
                                                                    ggprint( "{.2}\n", v3( 1.2345, 2.3456, 3.4567 ) ); // "v3(1.23, 2.35, 3.46)"
                                                                    

                                                                    a better string class (no allocations):

                                                                    str< 256 > buf( "{.2}", v3( 1.2345, 2.3456, 3.4567 ) );
                                                                    buf += v3( 1, 2, 3 );
                                                                    buf += 123; // "v3(1.23, 2.35, 3.46)v3(1.0000, 2.0000, 3.0000)123"
                                                                    

                                                                    a wrapper around Lua’s string matching code (no allocations!):

                                                                    for( array< array< const char > > matches : gmatch( "test1 test2 test3 test4", "(test)(%d)" ) ) {
                                                                            // matches.n = 2
                                                                            // matches[ 0 ] = { elems = "test...", n = 4 }
                                                                            // matches[ 1 ] = { elems = "1 te...", n = 1 }
                                                                            ggprint( "{}_{}\n", matches[ 0 ], matches[ 1 ] ); // "test_X"
                                                                            // trying to access matches[ 2 ] asserts
                                                                    }
                                                                    
                                                                    1. 7

                                                                      Don’t use RAII.

                                                                      Isn’t RAII a hugely important technique for avoiding code problems?

                                                                      1. 2

                                                                        Until it causes different problems, yes ^^;

                                                                        1. 1

                                                                          Like what?

                                                                          1. 2

                                                                            It’s hard to give specific examples of the problems in action but 1) you can pigeonhole yourself into writing code that’s ugly/inefficient, 2) it encourages managing resources in the leaves of your code which can be inefficient and difficult to follow 3) it’s viral - any structs containing RAII types become RAII types themselves, and 4) once classes start having meaningful constructors and destructors it forces a lot of complexity everywhere else. Your allocators have to start calling constructors and destructors, you have to be careful with memcpys and memsets, and not having to worry about that 100% of the time is a big win.

                                                                            I didn’t mean you should never use it, but I think it’s rarely actually a win.

                                                                            1. 11

                                                                              I’m not following you at all. From your submission history you seem to do mostly graphics programming, and I can buy that there may not be much use for destructors when you avoid dynamic allocation inside loops.

                                                                              Statements like “allocators have to start calling constructors and destructors” and “you have to be careful with memcpys and memsets” reinforce that you’re using C++ in some highly niche way. I don’t write allocators often enough that the amount of effort going into them is a data point for the language. And yes you certainly don’t want to be using memcpy in the presence of destructors.

                                                                              I went looking around the internet for critiques of RAII (because in 20 years of C++ programming I’ve never encountered criticism of RAII before), and found very few:

                                                                              http://coliveira.net/software/raii-is-overrated (2011)

                                                                              https://en.sfml-dev.org/forums/index.php?topic=9359.0 (2012) https://en.sfml-dev.org/forums/index.php?topic=19788.0 (2016)

                                                                              None of these is very compelling at all.. So I’m still flummoxed. RAII is one of two reasons I use C++. My code samples there show that I don’t even use the class keyword when given the choice. But you’ll prise destructors from my cold dead hands.

                                                                              1. 2

                                                                                From your submission history you seem to do mostly graphics programming, and I can buy that there may not be much use for destructors when you avoid dynamic allocation inside loops.

                                                                                You’re right with that, but even in random glue code I find that RAII can be annoying sometimes. It’s hard to explain without having specific code examples, and I don’t run into it in my home code so I don’t have anything to share. I also expect that std::move helps with some of them, but solving problems with language features with more language features seems iffy to me and we don’t use C++11 at work.

                                                                                you’re using C++ in some highly niche way.

                                                                                I’ve been writing C for longer than C++, so I mostly write C and add C++ where I think it makes sense. So I guess you’re right there too.

                                                                                1. 1

                                                                                  I’ve been writing C for longer than C++, so I mostly write C and add C++ where I think it makes sense.

                                                                                  Same here, so I totally feel you.

                                                                                  I don’t use C++11 either[1]. So I don’t think that’s an issue here.

                                                                                  [1] On a tangent, my policy lately has been to move to a standard only when it’s the default for the version of the compiler shipped with the newest version of Linux and OS X (LTS in the case of Ubuntu). I often get questions from users on older versions of compilers, so this seems a reasonably conservative line in the sand. For small projects like mine, losing a single person who might have tried them out is a big loss. Ubuntu 16.04 and OS X Sierra (both released in 2016) neither enable C++11 by default. So I’m still on C++03. In a year or three I’ll migrate Mu to C++11 and start using auto and range loops.

                                                                      2. 4

                                                                        As you say, to get by as “readable” you have to avoid a lot…

                                                                        If you need a language that is fast as can be and type safe, D is much nicer and more readable than C++.

                                                                        It’s like C++ but you can ask the compiler what it knows about any symbol and act on that knowledge at compile time.

                                                                        Template meta programming in C++ is a complete nightmare… You can even ask the guy who invented template meta-programming and wrote The Book on it.

                                                                        He went off and joined D instead since template meta-programming is simple, readable and straightforward in D.

                                                                        1. 2

                                                                          “Don’t use the STL. Don’t use RAII. Don’t follow “everything has value semantics and a = b does massive allocations and copies”. Don’t use the STL. Don’t use smart pointers. … Maybe use the STL if you can’t be bothered to implement things yourself, but measure your build times before and after.”

                                                                          Yes, definitely avoid exceptions if possible :)

                                                                          1. RAII is one of the best things about C++.
                                                                            This is amazing:
                                                                          database_connection db;
                                                                          if (db.open("127.0.0.1", port)) {
                                                                            ...
                                                                          }
                                                                          
                                                                          // Don't worry about closing the connection no matter how we return from this function it will close
                                                                          

                                                                          RAII should be exactly the same performance as the same code in C. The only time it should differ is if the C code avoided calling the “destructor” function before some of the return statements. You might have been confusing RAII with garbage collection or shared_ptr etc.?

                                                                          1. Compile times are good enough these days to use STL on all but the largest projects. Use STL until you find a place where it is slow.
                                                                          2. C++ users need to learn about reference vs pointer vs array, and move semantics, and then basically just wrap all pointers in unique_ptr or shared_ptr (And learn the difference).
                                                                          3. I would really try to avoid writing your own string class. Add some axillary functions to convert between your types to and from strings, sure. It is fun and isn’t too hard to create a string library, but in commercial stuff, there is a lot to be said of being able to bringing people into an organisation and having them be able to hack on the code in the first hour because they have used std::string/map/list/vector before.

                                                                          A better printf, use either of these:

                                                                          With streams you can do this type safely:

                                                                          my_log<<"x="<<x<<", y="<<y<<std::endl;
                                                                          

                                                                          With var args templates in C++11 you can do this also with type safety:

                                                                          my_log_function("x=", x, ", y=", y);
                                                                          
                                                                          1. 4
                                                                            1. IMO this is just as good:
                                                                            if( !db.open() ) return;
                                                                            SCOPE_EXIT( db.close() );
                                                                            
                                                                            1. I added iostream/string/vector/map to every file in my project (80k LOC) with gcc -include and my debug build went from 2.7 to 4.7 seconds. Less bad than I was expecting but still not great.

                                                                            2. unique_ptr is good but I find shared_ptr is a symptom of doing resource management in leaf code and losing track of/not caring what their lifetimes/owners are.

                                                                            3. Not such a big deal if it takes 2 hours vs 1 IMO.

                                                                            Format specifiers in streams are very very bad. They’re unreadable and they’re kept around as state on the stream to screw up your prints everywhere else when you forget to reset them.

                                                                            Writing my_log_function like that is good and better than C’s printf because you can add your own types to it, but I prefer the printf format + args style. If you’re feeling ambitious you can actually write a monster print that combines both styles, see autoprintf.

                                                                            Edit: if you do actually implement printing with templates it’s a big win to always disable optimisations to keep compile times down. If you want to do it cross-platform you need something like this.

                                                                            1. 2

                                                                              SCOPE_EXIT is definitely better than normal C goto or while/break.

                                                                              Yes shared_ptr can be a sign you are doing the wrong thing, especially if every single pointer in your application is a shared_ptr. In my code I find myself mostly using std::array, stack objects + references and occasionally unique_ptr which I am basically using as an “optional” return instead of returning a raw “null or object*” depending on if something was created or not.

                                                                              1. Yes, 2 hours is not a big deal even in the first week the developer has 40 of them :) I just mean it is nice that most C++ programmers have experience with the STL or have at least heard of it and know the basic concepts. Creating a string class or the other containers is ok, but you have to work hard to make it sane, predictable, easy to work with. The string library is not perfect but it is better than string libraries I wrote 20 years ago :)

                                                                              Yes format specifiers with state are the worst. You could possibly do something like:

                                                                              my_log_function("x=", FloatFmt(2, x), ", y=", FloatFmt(2, y));
                                                                              

                                                                              Or:

                                                                              my_log_fmt fmt;
                                                                              fmt.set_float_precision(2);
                                                                              my_log_function(fmt, "x=", x, ", y=", y);
                                                                              my_log_function(fmt, "z=", z, ", w=", w);
                                                                              

                                                                              I think I prefer the second one.

                                                                              Thanks for the optimisation pragma tips!

                                                                          2. 1

                                                                            While I do mostly agree. It could be argued that the resulting code is readable but not writable or maintainable unless the next coder is already a capable C(or worse, C++) programmer anyway.

                                                                            I myself ignore these concerns and write Objective-C++ on occasions when I think the performance boost is worth it.

                                                                          1. 2

                                                                            Chrome won just like IE won. Maybe it’ll last longer. But proposing standards, implementing them, and pushing them before they are actually standard is no different from what we used to hate MS for.

                                                                            (For the record, I prefer Safari because battery life and accessibility are more important to me than ..webrtc?)

                                                                            1. 10

                                                                              I think that “indexes” should start at 1 and “offsets” should start at 0. That is, I think a simple name change could fix the whole argument.

                                                                              1. 2

                                                                                [EDIT: This comment is largely wrong. Collections are 1-based, Streams are 0-based. It’d been so long that I’d forgotten. The point in general stands, but the code as-presented below will not work as expected; even when doing a range copy on a SequenceableCollection, it works 1-based with inclusive upper bounds. Doing a PositionableStream on the underlying collection will work as presented here. Sorry about that.]

                                                                                That’s exactly what Smalltalk does.

                                                                                c := #(a b c d e f).  "Declare a five-element array."
                                                                                c at: 1. "a"
                                                                                c at: 5. "f"
                                                                                c at: 6. "throws an error"
                                                                                c copyFrom: 0 to: 1. "#(a) EDIT: Nope, wrong!"
                                                                                c copyFrom: 0 to: 5. "#(a b c d e f) EDIT: nope, wrong!"
                                                                                

                                                                                This made instant and intuitive sense to me when I learned it as a kid in a way that 0-based indexing definitely did not, so I’m definitely on board with it. But I’d also note that in Smalltalk, as in many other modern languages, I barely ever even type the array index in the first place; doing things like first, last, allButFirst: 2, etc. are much more common. So I can’t honestly say it made a huge difference either way. (When I picked up Smalltalk’s successor, Self, I programmed in it for several days before I realized that its indices did in fact start with 0. And I only realized that because I thought I found a bug in the debugger.)

                                                                                1. 2

                                                                                  If you see everything as an offset from the beginning of an array, you can stop using the word index, and the confusion should stop.

                                                                                  1. 0

                                                                                    If you see everything as an offset from the beginning of an array, you’re thinking in terms of pointers and RAM blocks instead of thinking in terms of arrays. That abstraction slip will cause you problems.

                                                                                1. 1

                                                                                  I agree with his opinions on all but the search example, a big magnifying glass is just as easy, if not easier to see than the smaller “Search” blurb. The icon color in the case he chose could’ve contrasted more with the the background however.

                                                                                  1. 2

                                                                                    Part of the point was that the icon is completely unparseable to a screen reader. Some users have limited sight, for which better visibility is relevant. Some users have no sight.

                                                                                    1. 5

                                                                                      That’s why HTML has alt text

                                                                                      1. 3

                                                                                        I would suggest trying this in a screen reader (ChromeVox and Apple’s VoiceOver are both free), with your monitor turned off. Most web designers have no intention of doing that, so if they think about it at all they’re guessing how users will hear their UI, and then users are having to guess what the designer was thinking.

                                                                                        Using labels is a simple rule that is maximally explicit about what fields do what. Testing designs in screen readers would also work.

                                                                                        Alt text in this situation turns out not to be ideal, since the image is merely “near” the field in the structural layout, and not associated with it in a way that’s programmatically detectable. But the way to know is to try it, not to guess.

                                                                                      2. 2

                                                                                        Search is kind of a special case, as there is a semantic type=“search” for input tags

                                                                                        1. 1

                                                                                          Can you create a label that says “Search Query” for the search input, and hide it using “overflow: hidden” or something, and use alt text for the magnifying glass submit button that also says “Search”?

                                                                                          1. 5

                                                                                            You can use ARIA to add a lot of the hints needed. I’ve not seen a lot of front end developers make use of this.

                                                                                      1. 1

                                                                                        Sometimes compilers aren’t very good.

                                                                                        How much time would you have to invest in learning Assembly until you start to find inefficiencies in compiler performance?

                                                                                        Also, keep in mind that the abstractions you write for the compiler to work with could be a horrible solution to solving a particular problem.

                                                                                        1. 1

                                                                                          It’s usually not too complicated to spot, problem is the compiler generates an awful lot of assembly and you have to look in the right place(most of the output is optimal already).

                                                                                        1. 5

                                                                                          What kind of mad man wants to store his music library in MySQL?