News for hackers. Stuff that matters.
Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @11:13AM (#15542150)
> My opinion is that there is no "right way" of doing C++, unlike, say, python.

There are, however, definitely wrong ways of doing things in C++. There's quite a few things in the STL that should have long since been deprecated if not outright removed.

Simple example is std::map. Absolutely horrible data structure. std::unordered_map is what you actually want, but to a beginner nothing really tells you that. You just think "I need a map, oh hey there's a std::map, perfect!" and go about your day completely unaware of how awful a data structure you just chose.

Another example that's a mistake made in C++ hello worlds of all things is std::endl. std::endl does not mean "end line", it means "\n + flush" (specifically '\n' - it does not do '\n\r' or '\r\n' conversions at all, the underlying file system does that). And flushing on every new line only makes sense for line-buffered things like std::out, but std::out already flushes on '\n' character. So std::endl in practice just leads to a ton of unnecessary & unexpected flushes.

> so you go with new, but if you don't want to deal with exceptions or just want a block of raw memory, you may want to go with malloc instead.

There's actually very little reason to ever use malloc. If you globally don't want exceptions just use -fno-exceptions. If you don't want exceptions in the small scope you're working in then use std::nothrow, eg: "new (std::nothrow) int[1024 * 1024]".

As for why you do this over malloc the reason is simple - it avoids overflow bugs. new int[size_t] never overflows. malloc(size_t * sizeof(int)) - well that overflows trivially, and that overflow results in security bugs.

Raw malloc would arguably be another case of "this is just wrong C++" rather than "this is a perfectly valid alternative". Raw free would belong in this case except for calloc, which is still very useful (and avoids the common overflow bug that malloc has). malloc's only saving grace is realloc, but now you're pretty deep into a specific edge case.

Re:C++17 Is Here: Interview with Herb Sutter by smitherfield on Tuesday October 24, 2017 @01:30PM (#15543369)
Another reason to prefer `new`/`delete` over `[std::]malloc`/`[std::]free` (and `std::vector` over `[std::]realloc`) is that `malloc`/`realloc`/`free` don't call constructors and destructors, which means using them with non-POD types is UB (more concretely, you'll get bugs if the constructors or destructors manage some resource like a mutex, heap allocation or file handle).

As for outdated stuff in the standard library, that's something that affects every language as it ages. `map`/`set` aren't, IMO, the worst offenders; they were misleadingly-named (like `std::endl` and `std::move`), but RB-trees are a useful data structure, and while they are slower than hash tables (`unordered_map`/`unordered_set`) for the typical map/set use case, it's not like they're buggy.

The one that IMO most cries out for replacement is `[std::]rand` (inherited from the C standard library), whose RNG is abysmally low quality/predictable, very slow, and thread-unsafe. C++11 did introduce a new RNG API[1], but it's quite cumbersome and verbose to use, when they really need to introduce a simple API like BSD/Mac `arc4random_uniform`[2] so people will stop using `rand`.

Edit: I see that this is being addressed, although it didn't make it into C++17.[3]

While not buggy, iostreams are also getting a bit long in the tooth and could stand to be replaced with a more modern API, the iterator APIs (in the <algorithm> and <numeric> headers, etc) should also be methods on STL types, and `std::string` isn't quite deprecation-worthy, but it would be nice if the STL were augmented with a state-of-the-art encoding-aware string implementation like Swift's. And it would be nice if the STL acknowledged the existence of the internet (with high-level libcurl-like functionality in addition to the very low-level Networking TS).

[1] http://en.cppreference.com/w/cpp/numeric/random

[2] https://www.unix.com/man-page/freebsd/3/arc4random_uniform

[3] http://en.cppreference.com/w/cpp/experimental/randint

Re:C++17 Is Here: Interview with Herb Sutter by CoolGuySteve on Tuesday October 24, 2017 @12:13PM (#15542707)
std::map will often have better cache coherence, especially if you're using a custom allocator or have a small data set.

Also not sure what you mean when you say operator new never overflows. Can you elaborate?

It would be nice if C++ gave an operator new/allocator that put the object on the end of a page boundary so that the hardware could catch any overflows. Sort of like how the atomic operators are cross platform but hardware specific.

Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @12:37PM (#15542903)
> std::map will often have better cache coherence

std::map is a red-black tree. It will have among the worst cache coherence of anything. Do you have any supporting benchmarks?

And if you have a small enough data set then std::vector will run circles around any map anyway.

> Also not sure what you mean when you say operator new never overflows. Can you elaborate?

    size_t mySize = ...;
    int* myptr = (int*) malloc(mySize * sizeof(int));
    int* myptr2 = new int[mySize];
What happens when mySize is >= 1073741824 on a 32-bit system?

Hint: the malloc version has a security bug, the new one doesn't.

Re:C++17 Is Here: Interview with Herb Sutter by nzmsv on Tuesday October 24, 2017 @12:50PM (#15543005)
If mySize is computed at runtime, that computation should most likely be checking for overflow. If it is a constant, declare it constexpr and get a compile-time warning for the overflow.

In any case, it's the multiplication that has a bug, not malloc (though I agree that the usage you mention is unfortunately common).

Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @01:01PM (#15543103)
Yes it's the multiplication that has the bug, but malloc makes that error common as you said. An API whose usage commonly has trivial security bugs is not a good API. new just wholesale avoids that common category of bugs, so why would you avoid it in favor of malloc? They do the same thing, one just does it safer.
Re:C++17 Is Here: Interview with Herb Sutter by eco on Tuesday October 24, 2017 @12:50PM (#15543007)
Boost, loki, and I'm sure others have a "flat" map which is just an array backed ordered map. Slower insertions than the red-black map but because of the cache locality it's much faster overall.
Re:C++17 Is Here: Interview with Herb Sutter by je42 on Tuesday October 24, 2017 @01:17PM (#15543254)
actually, you should see the pain that you get when having unordered dictionaries by default.

which is the default in python, you get awesome effects, where the order of the unordered dictionary is dependent on the random seed.

where library implementor are serializing a dict into a serial structure (and are not sorting by key). running the same code and same data twice, will get you potentially different results.

now here comes the best. most of the time in python you don't see the difference. the output is exactly the same but never the less the order is not guaranteed.

so I believe having an ordered by default data-store will help the ecosystem.

Calling it unordered explicitly helps, to make sure that anybody serializing the structure will need to make a choice how to sort it.

Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @03:54PM (#15544713)
Even if you want ordered by default a red-black tree is a bad way to get it. JavaScript has ordered maps, but engines like V8 use things like this: https://wiki.mozilla.org/User:Jorend/Deterministic_hash_tabl... instead.

It's not a question of ordered vs. unordered, it's a question of red-black trees vs. a hashed array of some sort.

Re:C++17 Is Here: Interview with Herb Sutter by jnwatson on Wednesday October 25, 2017 @12:06AM (#15547383)
In Python 3.6, dicts are now ordered.
Re:C++17 Is Here: Interview with Herb Sutter by je42 on Wednesday October 25, 2017 @01:14PM (#15551947)
it's an implementation detail. not guaranteed. :( which makes this even worse.
Re:C++17 Is Here: Interview with Herb Sutter by slavik81 on Tuesday October 24, 2017 @12:42PM (#15542943)
A beginner may use std::map without reading even the first sentence of its documentation and get a red-black tree when they thought they were getting a hash table. The program will work correctly, but have a Big O complexity log(n) greater than they thought. That's not a problem worth breaking all the programs that correctly use std::map.
Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @12:55PM (#15543052)
> That's not a problem worth breaking all the programs that correctly use std::map.

I challenge you to find a single correct use of std::map to begin with.

Is this severe enough to be worth breaking programs? No. But was it a mistake? Yes, absolutely. And deprecation is how mistakes are fixed over time, so why not fix the mistake? Old things can stay on their old STL and live with the old std::map. Maintained things will get warnings that they can go address in their own time. And new things are guided to the proper choice.

Re:C++17 Is Here: Interview with Herb Sutter by zvrba on Tuesday October 24, 2017 @01:13PM (#15543222)
> I challenge you to find a single correct use of std::map to begin with.

Before C++11, you only had std::map, so every use was correct. After C++11, you may still care about iterator invalidation or actually maintaining ordering. These properties are too useful to be deprecated.

As for beginners, their priority should be learning the core language and making a habit of browsing the std documentation to see what's there. Then they'll stumble upon unordered map too.

EDIT: as for cache coherency, std mandates use of separate chaining. If you really care about cache, you'll have to implement open addressing yourself.

Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @01:27PM (#15543331)
> Before C++11, you only had std::map, so every use was correct. After C++11, you may still care about iterator invalidation or actually maintaining ordering. These properties are too useful to be deprecated.

There's no shortage of 3rd party map implementations. Nothing forced you to use std::map pre-C++11, so it still isn't correct to use std::map prior to C++11 it was just easier to use std::map prior to that.

If you need ordered access a hashmap + key list is a superior implementation to red-black trees, so std::map still loses there.

The only thing std::map does have is iterators stay valid during inserts, but this doesn't really seem useful and indeed it's something most maps in most languages don't have. Heck, Java's red-black java.util.TreeMap doesn't even bother to keep iterators valid during inserts even though it could. How is this a useful property in practice much less one "too useful to be deprecated"? Do you have any examples?

Re:C++17 Is Here: Interview with Herb Sutter by slavik81 on Tuesday October 24, 2017 @04:43PM (#15545087)
If we're just complaining about performance and third-party libraries are an in-scope solution, I don't see why anyone would settle for std::unordered_map. It trails on every hashmap benchmark. Chandler Carruth blames it on the C++ standard, which mandates chaining for collision handling.

I mean, I wouldn't complain if with C++23 we introduce std2 and make things better. While we're at it, we could also try to fix std::list and std::deque which are nearly useless too.

Re:C++17 Is Here: Interview with Herb Sutter by zvrba on Tuesday October 24, 2017 @01:33PM (#15543393)
> If you need ordered access a hashmap + key list is a superior implementation to red-black trees, so std::map still loses there.

Superior in what way?

> Do you have any examples?

It's a couple of years since, but I remember having relied on both ordering and iterator stability in certain algorithms related to topological meshes. Basically, I'd traverse a map and delete some elements during traversal.

Could I have done it in some way with a hash table? Yes. Would it have been more complicated? Yes.

Re:C++17 Is Here: Interview with Herb Sutter by kllrnohj on Tuesday October 24, 2017 @03:40PM (#15544575)
> Superior in what way?

Superior in performance, and you can pick if you want to optimize for iteration (put the keys in a sorted vector) or insertion (linked list). Either way you'll trivially beat traversing, inserting, and removing elements from a red-black tree in runtime.

> Basically, I'd traverse a map and delete some elements during traversal.

That works fine with unordered_map, too. You can keep the iterator when you remove. It's only inserts where map's iterators stays valid but unordered_map's doesn't.