9 ms·
Ill-Advised C++ Rant, Part 2
- johnydepp 11y agodo you realize this would crash with empty array: #define countof(X) (sizeof(X) / sizeof((X)[0]))
- kayamon 11y ago1) Don't use empty arrays. 2) So add a proper version to the language which works under all cases.
- GFK_of_xmaspast 11y agoLike a std::vector ?
- jupp0r 11y agostd::array has size() without any runtime overhead.
- tines 11y agoNo, because the expression is not evaluated at any time. Only the type of the expression is used. A much better implementation would be using std::begin, std::end; #define countof(x) (end(x) - begin(x)) (except for the double evaluation, but this is better as a template function instead of a macro anyway) because it'll work on any type that supports random-access iterators which include arrays but also std::array and vector.
- hendzen 11y agoThere is such a function in the STL already. http://en.cppreference.com/w/cpp/iterator/distance http://en.cppreference.com/w/cpp/iterator/distance
- stormbrew 11y agoCorrected below, leaving here for posterity and to make sure this conversation isn't confusing in the future. The only thing I'll note is that you get a compile error on a zero length array, so the OP of this chain turns out right in a way heh. Start ignoring here: Are you suggesting end() and begin() can be called on an array? As far as I know they can't. Apparently you can deduce array length in a constexpr function now in C++11 (which I only learned just now but also couldn't get working quickly, so have some salt with that), but before that arrays always degrade to pointers when passed as function arguments so there's (afaik) no way to extract their length from their type...
- hendzen 11y agoBefore the array decays to a pointer, you can get its length. Here is the implementation of std::begin for arrays in libc++: https://github.com/llvm-mirror/libcxx/blob/60d223df071f6e3d4ffa29331ed466fff563096f/include/iterator#L1430-L1436 https://github.com/llvm-mirror/libcxx/blob/60d223df071f6e3d4...
- stormbrew 11y agoQuite right, I'm apparently rusty on my stdlib knowledge. Didn't know about the reference trick.
- onedognight 11y agoYes you can call std::begin() and std::end() on an array. int a[] = {4,5,6}; for(int i : a) ; That is how the above works.
- kayamon 11y agoDoes this work at compile-time? One of the uses for countof is (somewhat ironically) doing a enum->string table where you want to enforce the table gets manually updated properly: enum Color { red, green, blue, num_colors }; const char *color_string[] = { "red", "green", "blue" }; static_assert(countof(color_string) == num_colors);
- tines 11y agoNo, it doesn't, which is why I said this is better implemented with a template :)
- im3w1l 11y agoI tried it and it works for me (gcc 4.8.4), but I couldn't tell you why.
- GFK_of_xmaspast 11y agoA bunch of those are of the form 'you know there are better ways to go about doing that sort of thing' (like 'maybe consider using a std::vector instead of that static array' and 'just make a minimal ctor for initialization, you'll save typing in the long run' and 'when do you need to know the largest value in an enum, and why can't you just toss in a dummy last element, also why aren't you using enum class') if not outright 'jesus, don't put yourself in a position where you'd want to do that' (like the 'iterate over members of a struct' or the type stuff). And I had never heard of those 'fourcc' codes and have no idea why anybody would want them in a language standard. That said, there are some reasonable points, like '#pragma once' and 'enum-to-string' (extreme disagreeage on the 'string-to-enum' direction tho).
- ryandrake 11y agoWhat possible use is there for being able to convert the name of an enum as a string that isn't totally bug-ridden the minute you write it?
- stormbrew 11y agoLog output.
- jjoonathan 11y agoSerialization. And don't tell me that it falls under "bug-ridden the minute you write it" because more defensive programmers than you and I have been using it responsibly in a dozen other languages for more than a dozen years.
- stormbrew 11y agoConstructors are often brought up as a reaction to C99 initializers, but they're really not the same thing at all until and unless we get named function arguments in C++. Constructors as they exist are equivalent to the normal initializer syntax, which C++ does support.
- jonstewart 11y agoI mean, finally getting standard networking and filesystem libraries is a really nice thing...
- maxlybbert 11y agoIt depends on your definition of "standard." POSIX is a standard, and it's the standard nearly everybody has used for networking and filesystem access.
- Q6T46nT668w6i3m 11y agoMicrosoft is a notable exception. ;)
- jonstewart 11y agoYeah, writing C++ code that works on both Windows and *Nix means I can't use straight POSIX, and usually wind up with Boost ASIO and Filesystem. Putting them into the standard library means I've got two fewer libraries I need to worry about in autoconf.
- porges 11y ago> Calculating the size of a static array. std::size is in C++17: http://en.cppreference.com/w/cpp/iterator/size http://en.cppreference.com/w/cpp/iterator/size
- Animats 11y agoI tend to agree, but gave up on fixing C++ a decade ago. I hope Rust is the future; it deals with all these issues. But Rust seems to be starting out at the complexity level it took C++ two decades to achieve.
- kibwen 11y ago> Rust seems to be starting out at the complexity level it > took C++ two decades to achieve. You say this every time that Rust is compared to C++ (which is a lot!), but I have yet to see an elaboration. What in particular are you talking about?
- berkut 11y agoAs someone trying to learn/experiment with Rust, the syntax is pretty complex. Now granted it comes with some benefits, and I realise there are a limited number of characters available to use (at least that everyone in the world has on their keyboards), but stuff like the lifetime char ' are IMO way too easy to mistake when quickly glancing at code for strings. But maybe that's just me.
- kibwen 11y agoDo you happen to be using a syntax highlighter that doesn't recognize the single apostrophe and therefore highlights a huge quantity of code as though it were a string? (This syntax is familiar to OCaml, so it's not like editors are incapable of recognizing this, it's just not usually the default.) This was an annoyance back before text editors had modes for Rust syntax, but I haven't seen it in the wild in years. If not, then it's simple to know when something is a lifetime vs. when it's a character literal, even if you're quickly scanning. Lifetimes appear in type declarations, so they'll be in struct definitions and function signatures. Character literals can't appear in those positions (and character literals are extremely rare in Rust already, so much so that in the past I actually petitioned to have them removed!). I also don't think that Rust's syntax is of comparable complexity to C++'s, e.g. the number of uses `const` in C++ (and on a technical level, Rust's grammar is much simpler than C++'s), so I don't think that syntactic complexity is what Animats was referring to.
- alschwalm 11y agoJust to note, the proposal the author lists as "so complicated" is actually just fixing an inconsistency in the grammar where "class" and "typename" are not always synonymous in the context of template declarations. Specifically: template<class T> class Foo; template<typename T> class Foo; Are the same. But, not when declaring a template with a parameter that is itself a template: template<template<typename> class T> struct Foo; // Compiles template<template<typename> typename T> struct Foo; // Does not compile The proposal allows the second form to compile, fixing this inconsistency.
- antiquark 11y agoHe has a some good points, but the "switch" statement needs an integer for a reason... a switch can be converted a jump table in assembly language, so that the "case" can be arithmetically determined and instantly jumped to. The reason being: MOAR SPEED! A jump table is a lot faster than a sequence of else-if statments.
- shiro 11y agoThe compiler knows the type of the value it switched upon and generates code accordingly (efficient code on int values, dumb if-chain on other values), doesn't it? Sure it may become hard to tell if the code is efficient or not, but that property has already long been lost with operator overloading.
- sago 11y agoI don't see why that follows. If switch is given an integer, the compiler could create a jump table, otherwise it can use an if statement block (or some other kinds of implementation). There's nothing in C++ that says certain statements must always have the same compiled code.
- Grishnakh 11y agoSo why can't the compiler be smart enough to use a jump table when this is possible, and fall back to a sequence of else-if statements when it's not? The compiler should even be able to make a more optimal sequence of assembly tests and jumps than by writing this code manually.
- maxlybbert 11y agoI found it funny how many of the complaints were really about the preprocessor. Many of them have already been addressed. If you don't like defining a macro as "do { ... } while (0)", try using an inline function. Even C has inline functions nowadays, and that's been true for more than a decade.
- chrisseaton 11y agoIf you could write it as a function why would you be writing a macro in the first place?
- maxlybbert 11y agoThat's an obvious question. A lot of macros should be functions. The most common case I'm aware of are functions like toupper(), islower(), ispunct(), etc. that the C Standard allows to be macros for performance (the actual work in the function is testing or setting a single bit, and the overhead of a function call really matters in that case). Inline functions would have the same performance, and you can get a pointer to them without doing the #undef rigamarole you see when a macro definition might muck things up.
- maximilianburke 11y agoInline functions aren't guaranteed to be inlined. There are compiler specifics that add stronger hints to the optimizer to inline but behavior isn't always consistent from one compiler to the next. The only portable way to force something to be inlined is to use a macro.
- chrisseaton 11y agoWhy is it essential that anything is inlined? Maybe if the compiler isn't inlining it had a good reason for that? Maybe it has some understanding of the trade off between specialisation and code size for these particular functions which you don't.
- chris_wot 11y ago1. Be more precise. You want the cardinality of the set of items of the array. When you say you want the "size", you sound like you want to know the physical size in bytes of the array. But yes, it would be good. But if you want to know why that macro is so problematic, have a read of the following: http://blog.natekohl.net/making-countof-suck-less/ http://blog.natekohl.net/making-countof-suck-less/ Of course, knowing how many elements are in an array is probably not a bad feature. (just noticed that porges points out that it's coming in C++17) 2. Completely agree with you on enums. There is a proposal in C++17 to allow for this, see: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n442... std::enum_traits<E>::enumerators::size The number of enumerators in the enumerator list of E. 3. Same deal, see in the same proposal: std::enum_traits<E>::enumerators::get<I>::identifier A std::string_literal(N4121) holding the identifier of the enumerator. The identifier is encoded in UTF8 format, with any UCNs decoded. 3. #pragma once... the entire way of including headers into code is kind of broken. Having to implement a compilation firewall (aka pImpl) just to ensure that when you change a private member definition you need to recompile all other classes that rely on it seems so incredibly broken to me. The LibreOffice code is littered with pImpls. It doesn't make it easier to read or understand the code, or even maintain it, at least in IMO. And without them, the compilation time is huge, every time I touch VCL code in anger I fear I'm wasting some other poor devs time in compilation time. 4. C99 designators - no opinion on this. 5. Binary - only if you can specify endianness. 6. FourCC doesn't seem like something for a standard... maybe that's just me though. 7. All macro criticisms - someone just please implement another macro processor in the standard already! 8. Iterating fields - back to that C++17 proposal again: std::class_traits<C>::class_members::get<I> Requires: I >= 0 && I < size Provides information about the I’th (zeroindexed) public member of C that satisfies the above criteria, in declared order. 9. Breaking by default in switch statements... ugh. Lots may disagree with me though. That .. gcc extension is pretty cool though! Add that to the standard, by all means! 10. Agreed on strongly typed typedefs 11. See that C++17 proposal I linked to previously - I think that has everything you'd want! High time too.
- kayamon 11y agoYep that sure looks like a fine C++17 proposal (although I didn't understand a word of how it would actually be implemented). How much do you want to bet that it won't be accepted? :). Like a lot of the other good proposals (std::optional anyone...?) I wouldn't be surprised if it never sees the light of day.
- wund 11y agoWho forgets to break in switch statements? I mean yea, there was a time when I started programming I did this once or twice but not for a quite long time. Seems to me more like a request for sloppy programmers than anything else. Also don't forget [Duff's device](https://en.wikipedia.org/wiki/Duff's_device https://en.wikipedia.org/wiki/Duff's_device) for how it could actually be expressive.