7 ms·
C++ Anti-Patterns
- sudeepj 6y ago> #pragma once Is there every a scenario where this is not needed in a header file?
- cozzyd 6y agosome headers are meant to be included multiple times with different definitions (although templates are usually a better way to do this).
- dkersten 6y agoYeah I used to see it from time to time, but it’s been w long time now. It seems to me that modern C++ doesn’t really need it unless you’re doing something like including data files (rather than source files) multiple times. Having said that, I almost did something like that recently, to initialise some generated data structures (include once for the structs and again to initialise memory pools), but I ended up finding a cleaner way that didn’t need it.
- OskarS 6y agoIt happens occasionally. An example would be that there are a couple of libraries that do "generic collections" in C using the preprocessor (instead of using templates). So, you could imagine something like this: #define VEC_TYPE double #include <c-vector.h> #undef VEC_TYPE and that would provide you a type like "vec_double", a vector specialized for the double type. You'd want to import that once for each vector type you wanted. It's not the most common use-case (and you can argue if it's a good idea at all), but it is at least a reasonable reason why you'd want a header file without #pragma once.
- glouwbug 6y agoYe, I pushed it to the extreme and back ported a good chunk of the STL to C: https://www.github.com/glouw/ctl https://www.github.com/glouw/ctl
- cozzyd 6y agostrong disagree on printf... maybe there's some modern typesafe C++ way to do the same thing, but format specifiers are way easier to remember than all the ways of modifying iostream.
- tcbawo 6y agoagreed. The printf style API is great for deferred/asynchronous log formatting via serialization.
- DerekL 6y agoThere's a new formatting library in C++20. Unlike printf, it's type-safe, and it's extensible to new types.
- jhalstead 6y agoIf you're interested, check out Abseil's StrFormat [0] for a typesafe printf replacement that uses format strings. You get a nice speed up as well. [0] https://abseil.io/docs/cpp/guides/format https://abseil.io/docs/cpp/guides/format
- cozzyd 6y agoThat looks pretty good! Too bad it's not part of the standard library.
- pvitz 6y agoI am not sure about his example of large classes. If "energy()" is an integral part of the model, why shouldn't it be a method of it? Maybe the example is not well chosen, but it looks more like extreme minimalism to me. If there are different kind of models (like Ising and XY), they could all implement the same interface with an energy method. Any views on this?
- wutbrodo 6y ago>If there are different kind of models (like Ising and XY), they could all implement the same interface with an energy method. Any views on this? If it's necessary for the interface, then it should of course be in the interface. But he was referring to the unnecessary placement of internal implementation functions into the class. This is all over the current codebase and it drives me _crazy_. In a healthy codebase, a header file is the best documentation yiu can have of a class, and polluting it with unnecessary functions that have no meaning to the user makes it harder to read and reason about.
- pvitz 6y agoI still don't understand why the magnetization or the energy of the Ising model should be "unnecessary internal implementation functions" when they are the actual outputs of the model. Thinking more about it, the example is (almost) a value object class. I don't think this is well-chosen for making a point about bloated classes.
- flohofwoe 6y agoIMHO most of those are not general "C++ anti-patterns", but instead define a personal C++ coding style (e.g. another C++ subset). It's important for teams to have such a coding style / C++ subset, but that doesn't mean that others are better or worse.
- MaxBarraclough 6y agoI'd say it's a mix, but most of the anti-patterns listed seem to be pretty 'objectively' wrong, and not sensitive to house style. They generally aren't just subjective preferences. My thoughts on the first few: Lowercase preprocessor constants. Doing this contradicts an established convention in C and C++, even used by the standard libraries. You simply shouldn't deviate from this convention, all you will do is introduce confusion. It's hard to think of a good reason to ever do this. Preprocessor over scopes. This generally confuses and complicates things, but makes sense in rare circumstances. BOOST_SCOPE_EXIT does something like this, for instance. Including source files (from a header file). Yep, that's a bad idea. I don't see why template specializations would be an exception. Unnamed #if 0 branches. This doesn't strike me as obviously terrible. Using meaningful indentation would help here. Introducing a macro constant strikes me as introducing a new kind of complexity, given that the goal is just to comment out code. If the goal isn't to just comment out code, but to do 'conditional compilation', then that's different. Standalone header files. Yes, a header file should include all the other headers it needs. Doing otherwise is sloppy, and no-one will thank you for wasting their time having to fix your headers.
- HelloNurse 6y agoAssuming that unconditionally enabled or disabled #if 0 and #if 1 sections imply some implicit flags or logic relating them to one another is particularly illogic. Unless the author has been exposed to someone who used them in complex ways as a poor man's version control system, of course.
- hctaw 6y ago> Unnamed #if 0 branches. This doesn't strike me as obviously terrible. Your editor has a key command to comment/uncomment a block of code with line comments, use it. Similarly, C++ has syntax for comments, use it...
- MaxBarraclough 6y agoShould this have [2016]?
- _pmf_ 6y agoModern C++ is the anti-pattern.
- bfrog 6y agoC++ is an anti-pattern
- carlmr 6y agoWhile I understand where you're coming from, we can't just switch every project to Rust.
- TheGrim-999 6y agoInterestingly, comments like the one you're replying to are the reason why I'm ignoring Rust. It's reached critical mass, meme status, where people will come in and shill for Rust or shill for how bad C++ is, not for any concrete or logical reason, but just because they've seen other people say the same thing. Which then, by becoming yet another person memeing that idea on, encourages more people to hold and repeat the same opinion, which then encourages more people to hold and repeat the same opinion, and so on. It's a viral idea, there's no stopping it at this point. The people repeating the same lines rarely have any actual reason to repeat them other than the fact that they think they're true since they've seen so many other people repeat them. They'll just come in and assert that "C++ is an anti-pattern", with no rationale, no facts, no arguments, it's more political than technical, it's a mantra they've been brainwashed into repeating since everyone else in their echo chamber is repeating it. Of course this same pattern of a viral idea/meme extends much further into politics and all other areas of our life, enabled by the internet / mass media. Maybe objectively C++ is pretty horrible and Rust is great, I'm not even saying anything about the objective reality of the situation. It's just so easy to spot the patterns at this point, across all areas of conversation, that I now just by default ignore and assume false anything that shows the same viral pattern, because I know the viral pattern is usually backed by nothing but itself. Maybe in the past if enough people believed something to be true it'd be easier to assume there's some reason for them to believe that, but in the era of the internet / mass media that's pretty much gone.
- bfrog 6y agoI've worked on large C++ projects. Debugging hard to find data corruption, data leaks, data races was one of the most frustrating aspect. In a 200kloc project with 10 devs, there was always a crash worthy bug. Always. Automated tests could only catch so many things. Often the crash made it to the end user, causing them to lose valuable work and time. It sucked. By far though some of the worst issues were ones involving heap fragmentation because of all the damn heap loving C++ classes and shared_ptr wrappers. Rust doesn't solve everything. But it solves the most time consuming parts of C++ for me. Debugging. I've yet to need a debugger for Rust, after 5 years writing many 10s of thousands of lines of it. I trust the random crate I pull in won't segfault on something asanine if it stays away from unwarranted unsafe usage. It's a breath of fresh air to not need a GC, be able to trust most other code won't cause a crash, and that once it compiles it's most likely going to work barring any logic bugs. It saves me immense amounts of time, and mostly avoids debugging hard issues in production. Mostly. While people sit in their debuggers adding watch points, stepping through some 3rd party lib to figure out a crash, I'm busy working on the next killer feature.
- hctaw 6y agoMost of these are just mistakes you see in code written by novices or non-professionals. Although its symptomatic of a larger issue when working with production C++ codebases, which is that you need a lot of eyes on the code to prevent these mistakes and automated tools only go so far. As always, -Wall, -Wpedantic, -Wextra, and -Werror are your friends. But you should understand the error before you fix warnings (which is what `double x = double(1)` looks like)
- SloopJon 6y agoThe advice about the order of includes is interesting, as it's the exact opposite of my usual habit. I see the point, and I'll definitely consider adopting it. One thing I've been bitten by in the past is that Clang / libc++ on Mac sometimes doesn't require a standard header that GCC / libstdc++ does. I don't think this pattern will fix that, though.
- zabzonk 6y agoThe C++ Standard says that for any implementation, any Standard header file may itself #include any other header file, or not. It's thus up to the programmer to always #include the headers for the Standard functions they use, if they wish their code to be at all portable.
- dbattaglia 6y ago“I have asked why he did not just write T t; and be done with it. He said that he is used to the T *t = new T(); syntax. Then I pointed out that he has a memory leak. He replied that the runtime will take care of that. Too bad there isn't a runtime in C++ ...” Yikes! I don’t know if I’d call this a “not using RAII” anti-pattern, it’s just sheer ignorance / incompetence of how the language works.