5 ms·
Comments like this perplex me. I am a full time C++ dev and I could just rewrite your comment woving the "++" to the other "C". Really like that I can create a
by sqeaky 10y ago
Comments like this perplex me. I am a full time C++ dev and I could just rewrite your comment woving the "++" to the other "C".
Really like that I can create a class that stores all the knowledge of one concept internally and if I wrote that correctly I never need to look inside it again. Even better, if I document the contracts of using a class I can carefully optimize it and have broad performance effects with small code changes.
Things like std::string are just so much easier to work with that their C counterpart and things like std::filesystem::path simplify (or use the boost one if you don't have C++17 yet) so many things and doesn't even have a C counterpart. I point these out as simple examples but in all the code bases I have worked on there are similar examples, like most games a class to represent 2d and 3d points, which are used to define AxisAlignedBoundingBoxes, which are needed for collision detection algorithms which them selves need several classes to describe.
Then I can build systems of a size and complexity I literally could not comprehend without those abstractions. And then the compiler enforces them for me so other people can use them safely as well. Why is it so much harder to do this in C if C is so much "simpler".
- btilly 10y agoC++ has the issue that there are a lot of ways to hide magic, and a lot of hidden magic can blow up in unexpected ways if it interacts badly with other parts of the language that you do not understand. The result is that you really need to stick to a subset of the language that has been chosen to work well together. Safely adding to that chosen subset is challenging. And it just takes one developer to create a major headache. See, for example, https://google.github.io/styleguide/cppguide.html#Exceptions https://google.github.io/styleguide/cppguide.html#Exceptions documenting why Google is not willing to allow even something as basic as exceptions to be used in C++ code.
- sqeaky 10y agoEvery language can hide stuff. Classes, templates and operator overloads are just pretty wrappers for functions with better designed conventions for the common cases. Anyone can write "hidden magic" in any language with anything like these abstractions. It is particularly easy to make this hidden magic in C. For example, there is no way to express who has ownership of a pointer or when a function expects a pointer or an array. I have seen plenty of C libraries that document things like this, but for each that does there are 3 that don't. For each one does document things like that, they do it differently with different conventions but for the same reasons, but I cannot even use common idioms to be safe I must understand every part of each library I call. It is much more clear what is going own in C++ when a function accepts or returns a std::unique_ptr and I cannot screw it up without trying hard. What seems more important to me is exposing the relevant parts of the software when needed. If I care about the business logic (Even if its nots a business app... HP and Mana can be considered the business logic of a game) it can be hard to tease that out when lots of "hidden magic" is shoved in my face. But when I need to handle a new file format that the business logic requires indirectly I don't want to mess with the business logic. Having more tools to cleanly express this helps. So have a type that handles this IO while some other type handles business logic is indeed changing magic into "hidden magic", but it is also enforcing separation of responsibilities. Something much harder to do when your only real tool of abstraction if functions. The only people I have met in real life that stick to anything like the "hidden magic" argument are the same people who advocate for single large functions. These people like their function on the order of hundreds or thousands of lines so they can "see everything". You aren't doing that are you?
- btilly 10y agoThe problem is not so much hiding stuff. It is being surprised by the interactions between features. The "can't get there from here" like trying to printf to an iostream. If you're not careful, simply scanning through a large file is several times slower than other languages. Template magic makes seemingly reasonable type names blow up into insanely hard ones to figure out. There are non-obvious patterns that you have to know such as RAII. And good luck if you want to make things portable! I remember at Google being asked how I checked in unit tests that broke integration tests. Turns out that GCC and Clang disagreed on whether a friend of a subclass can see protected elements in the parent class. The local language lawyer decided that gcc was right, sent off a bug report to Clang, and I had to make my little unit test class a friend of the parent class as well. Maybe I was unlucky, but why does this sort of thing not happen to me in other languages? In other languages I am consistently able to read up on the language syntax and features, implement things within my knowledge, catch my bugs with unit tests, and have a basically working system. I've had this experience with C, Lua, Perl, Ruby, Python, PL/SQL, Java, JavaScript, etc. But C++ finds ways to astound and surprise me. Perhaps if I was a full-time C++ developer I'd learn all of the corners and would simply be productive. But the last time that I wrote a non-trivial C++ program, there wound up being multiple things which worked right in unit tests but not the full program until I ran Valgrind and followed suggestions that I would have had no way of figuring out on my own. Yes, I'm aware of how easy it is for third party libraries to be bad. From dangling pointers in C to monkey patching in Ruby there is a lot of crazy stuff that can be screwed up by third party developers. But C++ is the only language where I had trouble not screwing myself up.
- sqeaky 10y agoPhrased this way your concerns seem much more valid. As for C++ file IO, I think it sucks too that something as idiomatic a iostream iterators are pretty much garbage. I hope they are removed in STL2 and they keep the good parts of iostreams and ditch the bad. With tellg and seekg you get the same or better performance as the c lib functions unless you need to care about visual studio performance... but if you are using that compiler you never actually compared enough about performance to benchmark. I feel I must point at that C features shouldn't be expected to interact with C++ features. Printf does what it does, it was not designed to live with objects and types. It is a holdover from the C days. It is totally unaware of non-trivial types and does really gross things when you give it the wrong type. Using some other function to write to something that can be streamed or writing and operator<< overload for the the thing you actually wanted to output seem like the simplest approaches. As for finding implementation specific bugs, you claim not to have found them in other languages, then included Javascript on a list of things you have used. Javascript is the poster child for implemtation specific problems, to the point where there are several sites that put real effort into describing differences between different implementations. This whole listing seems odd. These languages either have 1 implementation (Lua) so cannot have differences or are so under-specified that of course the implementations have huge differences (Ruby, Python, Sql). Clearly I have had more problems with all these then you I find all languages terrible at this point, I just a few to be less terrible. I think I may know why you are shooting yourself with C++, as you put it. If you find RAII non-obvious after you have worked with it then there is definitely a problem with how you are approaching something about C++. RAII, or deterministic deconstruction in general, is probably the single strongest thing the C++ language brings to the table. With RAII you can implement your own memory safety model via any kind of shared pointer you can dream up. With RAII you can prevent race conditions by creating exception safe mutex guards. Write your own transaction handler by putting the roll-back code in the deconstructor. With RAII you can clean up any kind of resource in a deterministic and safe way that few other languages offer. I had to some work with automated UI testing recently (a complex application and framework with C++, Java, Ruby and Python). My application leaked resources in a very gross way because we had to pass handles to our resource back out to users writing scripts that could manipulate them. This leaky resource was whole web browsers. For a combination of technical and business reasons the only suitable tool for creating browser instances. If I could have relied on Java's finalizers to be called I could easily have close them there. We found several situations where they clearly failed and much documentation about the reason they failed (and apparently how the JVM could be fixed if the standard authors were so compelled). After a couple of weeks of research and failing to be able to explain the various segments of the Java community the "using" keyword was inadequate for this usage pattern, the smallest hack we could come up with was a silly watchdog timer that checked the processes on the machine and knew when the web browser manipulation API was used. This was almost a 1000 lines of code to get right to buy nothing but resource safety in the face of exceptions. It would have been 4 lines of C++ and two of those would have been curly brackets. Of course I am biased, I pick a story from my experience to suit my argument as you have done yours. I am still not sure how one hurts themselves more with C++ than particularly and if you have access to C++11, C++14 or C++17 it seems a fair bit safer than Java or Ruby because of the precise guarantees and strong tools for safety the language lacked before. Still can't keep up with With Rust or Haskell in the safety department though.