| ▲ | samiv 6 hours ago |
| If you don't use the STL you end up re-implementing it yourself. Usually poorly. |
|
| ▲ | flohofwoe 6 hours ago | parent | next [-] |
| > Usually poorly. On the contrary. You can focus exactly on the features the higher level game code needs. The C++ stdlib is (for the most part) poorly designed, usually poorly implemented, the main reason for slow build times, and its complexity explodes because it needs to consider all edge cases that most code bases don't ever trigger. A specialized dynamic array class in a few hundred lines (at most!) and with just the required features is much more useful than the 20kloc monster that's pulled in with `#include <vector>` and which doesn't even do bounds checking in the 'idiomatic' usage. |
| |
| ▲ | Chaosvex 6 hours ago | parent | next [-] | | Saying it doesn't even do bounds checking (in release builds) is to miss one of the major points of C++ - not paying for what you don't need. It's not a mistake, it's a feature. You complain about it not being suitable for game development in one comment but then expect bounds checking in release builds? You're sitting in multiple lanes at the same time. NIH implementations are usually grossly inferior because as it turns out, it's quite hard to get it right and those edge-cases aren't important until you start getting bitten by them when you'd rather be shipping features. | | |
| ▲ | flohofwoe 6 hours ago | parent | next [-] | | > bounds checking in release builds Bounds checking overhead is negligible for all but the absolutely hottest code paths (fwiw we shipped active asserts, including bounds checking asserts in all the PC games I was involved with - carefully monitoring the overhead of course). The main reason to not use the stdlib isn't so much about squeezing out the last bit of performance, but about control of what actually happens under the hood (and also compilation times). The overall runtime cost of all those active asserts (not just the range checks, everything) was somewhere in the 2..3% range, which is fine when budgeted for upfront. | | |
| ▲ | Chaosvex 6 hours ago | parent [-] | | That's your opinion, others won't agree and would much rather not pay the price at all. | | |
| ▲ | imtringued 4 hours ago | parent [-] | | Those asserts probably saved a lot of development costs and increased the robustness of the software, which is worth a lot more than a few percent on a benchmark. I personally am more conservative on those things. I'll pick the fastest thing that is reliable. | | |
| ▲ | bluGill 3 hours ago | parent [-] | | Are we talking about games or medical devices here? I expect different things from them. If a medical device needs to turn off bounds checking to get results I'm concerned enough to not want to let anyone use it. If a game can get a slight performance improvement I'm all for it, who cares if it crashes, it is just a game. |
|
|
| |
| ▲ | criddell an hour ago | parent | prev [-] | | Sometimes there are ways of getting runtime bounds checking. For example, both of these return the 3rd element of a std::vector: auto val1 = vec[3]; // no bounds checking
auto val2 = vec.at(3); // bounds checking
| | |
| ▲ | Mond_ 29 minutes ago | parent [-] | | Yes, with the trade-off of essentially requiring exceptions, which are also banned in some codebases. |
|
| |
| ▲ | samiv 6 hours ago | parent | prev | next [-] | | Yes I don't disagree that sometimes a specific container or a data structure is great for the problem. Problem is that most of the game code and related code (so tooling,editor, auxiliary engine code) does need a typical STL type functionality and then when the org has "omg no STL" blanket rule someone ends up implementing STL and that's almost always worse than the STL that ships with the tool chain. Even worse..it'll be missing features and data structures and then people have to write sub-optimal code to work around it's limitations. | | |
| ▲ | bluGill 3 hours ago | parent [-] | | Top tier game orgs are often large enough to have good people write their own library with the correct compromises. They also tend to need micro performance improvements enough to be worth it. Most of the rest of us STL is good enough. |
| |
| ▲ | demorro 6 hours ago | parent | prev | next [-] | | I find it hard to agree that the stdlib is poorly designed and implemented. In my entire career it has pretty much worked entirely to spec. Yes, it can exhibit non-optimal performance, and in some specific cases (regex's especially), extremely poor performance, but that's not the same as being poorly designed and implemented, especially given the breadth of the thing. | |
| ▲ | pjmlp 5 hours ago | parent | prev [-] | | Which is why one of the security measures in C++26 is to make bounds checking idiomatic, finally. |
|
|
| ▲ | mixolydianagain 6 hours ago | parent | prev | next [-] |
| some of the STL is easy to improve on. For example, std::unordered_map performs poorly due to pointer stability requirements in the standard. Most performance sensitive C++ codebases will use something like abseil's hash maps instead. |
| |
| ▲ | spacechild1 5 hours ago | parent [-] | | Just a heads-up: if you're already using boost, boost::unordered now has open addressing containers (unordered_flat_map and unordered_flat_map) and they are among the fastest. | | |
| ▲ | ahartmetz 3 hours ago | parent [-] | | Seconding this - boost::unordered_flat_map was only added in December 2022 and many people don't know about it yet. |
|
|
|
| ▲ | VoidWarranty 6 hours ago | parent | prev | next [-] |
| Which is worse? std's mess or one you control? I'd take any random game engine's STL over std any day. |
|
| ▲ | leonidasrup 6 hours ago | parent | prev [-] |
| C+ Standard Template Library is the best designed part of C++ library. It was designed by Alexander Stepanov. https://en.wikipedia.org/wiki/Alexander_Stepanov |
| |
| ▲ | tialaramex 5 hours ago | parent | next [-] | | So, a few things (aside from the whole nomenclature argument already in another reply) 1. Stepanov's generic programming is a good idea. Every language you've seen with "generics" that's his idea, to the extent "The STL" is generic programming, everybody agreed it's a good idea. 2. But the STL is very old now, so while the idea is good, this is one of the oldest (Stepanov had tried this in other languages before C++) implementations and so other implementations are often better, because they've learned from experience 3. As well as pretty good generic algorithms, the STL also provides a lot of container types (what Rust would call collection types) and these vary not between "excellent" and "mediocre" but between "mediocre" and "inexplicably terrifying". The most charitable explanation is that they're just intended for teaching. If you teach DS&A to a Computer Science class you want the Extrusive Doubly Linked List to teach in class. If you write software you almost certainly never need this type, but it's front an centre in the C++ STL. There's a single "I guess I would use this" container type, std::vector. It has an insane special case for bool, because WG21 are idiots, but it's otherwise a good enough growable array type and it's not worth building your own instead given the constraints. Everything else is silly, or bad, or both. std::unordered_map feels like a hash table I made in class in the mid 1990s, but it's actually the provided standard hash table container in C++ 11 onwards. std::list is just that extrusive linked list for some insane reason. The Microsoft standard library maintainer STL could not offer me any justification for what std::deque is actually supposed to be for. | | |
| ▲ | bernds74 4 hours ago | parent | next [-] | | I would argue that even the basic concept behind STL is misguided. The rationale I often see is "you only need N algorithms for M container type, instead of N*M". This ignores the fact that algorithms and data structures are not independent of each other, and also that most of the time these days you're operating on vectors, so M ~= 1. Case in point: list::sort. You don't want to try running quicksort on a linked list. Or remove_if: great we've abstracted the difficult task of removing things without erasing them, except we can't do it on maps. (C++20 seems to add an erase_if, apparently admitting that the two-step remove/erase is silly). Then there's the fact that C++ iterators are basically pointers into the data structure, where for vectors (your common case) you'd do much better with index/container pairs, both for stability and bounds checking. | |
| ▲ | ahartmetz 3 hours ago | parent | prev | next [-] | | AFAIK, std::map is also OK for what it is: an ordered, node-based (tree) map. These are (almost) always slower than hash tables. Of course, std::unordered_map, the std hash table, sucks because of unforced errors. For that, there is boost::unordered_flat_map. | |
| ▲ | einpoklum 4 hours ago | parent | prev [-] | | > There's a single "I guess I would use this" container type, std::vector. About that one... I would claim that in a majority of cases where an std::vector is used, what the author really wanted was a similar type, but whose size and capacity are fixed on construction and never change. The standard C++ library does not offer such a type - so people use vector because it's handy. Agree with your takes on most of the containers. I also dislike how optionals are never used with containers as they were standardized later (and even then, problematically w.r.t. references). Thus, for example, if I lookup an object in a map of T's, the result should IMNSHO be an optional reference to a T. | | |
| ▲ | Joker_vD 2 hours ago | parent | next [-] | | > a similar type, but whose size and capacity are fixed on construction and never change. There is std::array for that. Also, for a type with fixed capacity but variable (up to that capacity) size, we're getting std::inplace_vector soon™. | | |
| ▲ | einpoklum an hour ago | parent [-] | | std::array requires the size to be set at compile-time, while I was talking about arrays whose size is determined at construction-time. Of course std::array is also quite the useful class :-) |
| |
| ▲ | afdbcreid 4 hours ago | parent | prev [-] | | What operations could such frozen vector offer that std::vector does not? If there are none, it doesn't need a separate data structure. | | |
| ▲ | einpoklum 3 hours ago | parent [-] | | Oh, on the contrary, the separate structure is needed and useful because it offers _less_, not more: * APIs/function signatures explain more clearly what are the intended uses of the structure that's passed. * More potential for compiler optimization * Some potential for having these on the stack (if the compiler deduces the size already at compile-time) * More convenient for static analysis * No plethora of confusing constructors (including the infernal two-element ctors which can be misinterpreted super-easily) etc. |
|
|
| |
| ▲ | einpoklum 4 hours ago | parent | prev | next [-] | | It has some very useful principles, but also some super-annoying gaffes and mis-design aspects. One example: Allocators. What a mess! Or the fact that if a map lookup fails, an exception is thrown. I can't count the times I've had some app just bail out on me with an at() exception, because the author neglected to handle it (and the map/unordered map interface did not force them to). That does not detract from Stepanov's important work. | | |
| ▲ | ahartmetz 3 hours ago | parent [-] | | The kind of programmer who don't check (or think through so that they can't fail) their map lookups is also the kind of programmer who don't bother with const. What a non-const unchecked map lookup gives you is a default-constructed value that has just been inserted for the only reason that operator[] returns a reference, which must "point" to something. That's bad and can be confusing, but it doesn't crash. I see that problem much more often than crashes due to unchecked map lookups in production, which are very rare for me. Less than once a year. |
| |
| ▲ | pjmlp 5 hours ago | parent | prev [-] | | Nowadays also used by many of us (wrongly) to refer to the overall C++ standard library, instead of what was inherited from C. |
|