HNHacker News
TopNewBestAskShowJobs

quicknir

205 karma · joined May 9, 2015

submissionscomments
quicknir··on Announcing Rust 1.11
It's not really true that C++ does not have an ABI advantage over Rust if (by your own comment) they both end up using the C abi. Although pedants will insist that C is not a subset of C++, practically speaking you can write idiomatic C perfectly fine that will compile with a C++ compiler, and much C code in the wild is actually written this way. And most moderately experienced C++ developers are very comfortable with all of these elements and syntax (like pointers, goto, C style callbacks, etc), because they are a subset of their own language. It's very straightforward to have your API be C (and therefore have a stable ABI) but do implementation in C++. Even some C standard libraries do this.

In summary, if you're writing a C API/ABI, source compatibility is still a major advantage, there is no two ways around it.

quicknir··on This Is Your Life in Silicon Valley
Most of my good friends, let alone Facebook friends do not share similar views. Some people are pro legalizing drugs, some are anti, some are more socialist whereas some are free marketeers, some are pro foreign intervention and some are isolationist. We don't spend most of our time talking politics and it just doesn't matter that much.

If you don't have any friends with different political views, this is an unlikely enough occurrence via chance that it seems like it's a deliberate choice, and that's not a choice that I have anything good to say about.

quicknir··on The Canadian Housing Boom Fueled by China’s Billionaires
No that's not all it is, and the person who does not understand the law is you. Quebec language laws require for instance that commercial signs not only have French, but that French be predominant. Why does a sign having big print in English and small print in French, prevent a native French speaker from working there?

Also, realistically if you hire many programmers who only speak English, it's simply not realistic to hire a programmer who speaks French exclusively because he can't communicate with the team. Putting "copiez" on your photocopier does not alleviate that.

These laws are discrimination, pure and simple, and have been found as such by the Canadian Supreme Court on many occasions. Most Quebecois just don't care about paltry things like individual rights, where their language is involved.

quicknir··on The Canadian Housing Boom Fueled by China’s Billionaires
Actually, it is "asking a lot". Quebec's language laws have been found to be in violation of the Canadian Charter of Rights and Freedoms. Unfortunately, the Charter (which Quebec did not support) has a "notwithstanding" clause, which Quebec invokes more often than all other provinces combined.

Quebec is a joke, both economically and with respect to liberal democracy.

quicknir··on Practical Guide to Bare Metal C++
Throwing from a destructor in ScopeGuard is equivalent to calling a function that can throw in a catch or finally block, which you can do in most (any?) languages with exceptions. This no exceptions in destructors "issue" is not a C++ issue. It's a fundamental issue in error propagation. What do you do when propagating error correctly, causes a new error? You can't propagate the first error, because you can't do it correctly. You can't propagate the second error, because that would mean dropping the first.

Classic example is logging an error on failure. This means calling a logging function in the catch block, and then letting the exception propagate. But what if the call to the logging function fails? In Java, coded naively you'd simply drop the original exception. Usually that's not what you want.

You can examine the issue with error codes, it's not any better.

quicknir··on How China is rewriting the book on human origins
I don't know what you mean by "definitive conclusions". Any conclusions that will be made will naturally be statistical in nature, and obviously the boundaries between groups are fuzzy in many cases. That does not mean that groups do not exist. It's not a coincidence you can pass people by on the street and readily identify 95% of them as being "white", "Asian", "Indian (/Pakistani/etc)", "black", etc, and that if multiple people perform this task, the agreement will be very high. It's obviously shared genetics that are causing these groupings, and if shared genetics are leading to statistically significant identifiable observable characteristics in one area (appearance), it's reasonable to ask if they are leading to other statistically significant differences.

These differences are already used very widely in discussions about policy, as you noted, and that's exactly part of the reason why it should be fair game to fully investigate those differences.

quicknir··on Practical Guide to Bare Metal C++
I don't agree with much in this post, but in particular "Destructors is all we have": destructors + lambdas + templates allow you to write ScopeGuard, which is a pure superset of finally blocks. Modern C++ has zero need for finally.
quicknir··on Tech layoffs more than double in Bay Area
I know people at most of the larger algo trading firms in nyc, they all have all excellent reputation for how they treat employees. Sounds like you have algo trading confused with big banks like GS.
quicknir··on Joint Allocations in C++
It's not really an extra pointer, unless you assume there are no alignment issues. If you assume that there's zero padding between members, then yes, you can do 1 pointer for storage + 1 per view. In the follow-up post, I plan to either do one pointer per view, or one integer per view, haven't decided which.

The thing is, who should solve it? The different approaches you listed have different advantages, there isn't one right answer. If the language itself solves it for you, you are stuck with whatever solution the language picked. This would be fine in a higher level language, but not in C++.

You're right though that individual devs shouldn't be solving it, it should be in a library. If there's enough interest in these posts, I'm happy to put up my work (fully fleshed out and documented) on a github for people to use.

quicknir··on Joint Allocations in C++
Thanks, I appreciate that. I welcome your feedback on the follow-up as well.
quicknir··on Joint Allocations in C++
FWIW, I assumed as much when I wrote the post. I wrote merely that the code is bug-prone generally, without pointing out specific bugs. A stance that I got the impression you agreed with, from watching your video.

In generally I tried to avoid an overly negative tone when referencing your code or statements, I hope you find that it's reflected in the post.

quicknir··on Joint Allocations in C++
I think that you are misattributing premises to me.

I think that a "combined type", can be safer if well written than a raw pointer. Surely you don't disagree with that? That's not the same as saying all combined types are better.

Similarly, adding abstraction can be good, it can also be bad. C adds abstraction to assembly. I'm guessing though that you would choose C over assembly for many things?

Repeating yourself is a bad thing in general. It could be that to get rid of the repetition we'd have to incur other costs that might be worse. I don't think that's the case here.

I just don't understand your "magical create_contiguous_memory" comment. Do you feel like the sort function is magic, and instead we should write all of our sorts out at the call site? More practically, we write a good sort once, document it carefully. The users of sort know roughly how it works and exactly how to use it. In the rare cases they care, they read the code. make_contiguous is exactly in this boat: you write it once, you write it carefully, and then you use it without worrying about the details every single time. Our brains are just too small to deal with all the details all the time, that's why we're trying to hide complexity that isn't as immediately relevant.

Your post is mostly sweeping generalities which aren't true in general, and as far as talking about the code goes, you mostly just say you don't like it or find it hard to read, and brush aside anything concrete like reusability, testability, bounds checking, etc, specific things that are actually useful.

From an ok, not great programmer with a year's experience, I wouldn't necessarily expect them to fully understand make_contiguous right away. But I would expect them to understand who owns the memory, and the memory layout.

quicknir··on Joint Allocations in C++
Hi, thanks yassim. Yes, the use case is from the original article. Yes, that is certainly another way to go, you could just provide the unique_ptr and a bunch of integer offsets. In this post, my data structure is trying to balance performance and code cleanliness, it's advantageous to provide standalone ArrayView which can't just be two integers. In the follow up post I'll be trying to make the data structure itself generic, so it will more easily allow for space optimizations.

I guess we'll agree to disagree, I think that C++ gives us some nice ways of writing it, I think that's what the post shows :-)

quicknir··on Joint Allocations in C++
I did discuss stateful allocators. They're nice, I'm a big fan. But as I said, they would impose pointless space overhead of one extra pointer per array. Also, you now have more complicated issues: each of the arrays is now an owner. What if you try to make a copy of it? Move construction? What if the container tries to deallocate, how does our allocator handle that? When I copy Mesh, would I need to change the allocator's state in the copies? How would that even work? I think that once you think about trying to accomplish the very specific and relatively simple thing being done here with allocators, you'll see that it would be quite a bit more complexity to no benefit.

I think I did give it a "little" discussion :-). Guess it depends on your definition of a little. I didn't want to talk more about it because I didn't want to get sidetracked, and this is ultimately the way I chose to do it.

Hope that sheds some light on the post and the choices I made with it.

quicknir··on Joint Allocations in C++
The point isn't necessarily about whether C++'s abstractions are faster than Java's, the point is more that you aren't always forced to use them. If you are willing to commit to the size of your string at compile time, you have the option to use char[N] instead, avoiding heap allocations. Or you can use a stack-based allocator for std::string.

The thing about templates is misleading. Sure, if misused they can hurt your cache. But they also move branching from run time to compile time, which is a very good thing. Google some benchmarks of C++ sort vs C qsort; the former is much faster because the comparator can easily be inlined.

The templates used in my post, for example, either bloat the code not at all, or very little. They are either generating tiny functions that get inlined anyway (like ArrayView; once a function is inlined its irrelevant whether it came from a template or not for code bloat purposes) or they are generating code that would just need to be written by hand (like make_contiguous).

I've actually personally witnessed two fairly detailed accounts of people that actually got themselves into situations where template or template-like bloat became a performance negative, relative to the benefits they provided. They didn't cite a cliche about code bloat, they actually benchmarked and found the cost. What both of them were doing was very extreme; nobody who's not using very extreme template techniques (and therefore is sold on it) is hitting that point.

quicknir··on Joint Allocations in C++
I considered that. The problem is that memory_block would need an extra pointer to know its size, which seemed like pure waste at that point. And ultimately it doesn't relieve you of writing copy constructors etc which should be the real goal; it scales much better if you have a class with other members that are not a part of all this contiguous stuff. I hope to solve that in the next post. I think your approach is good as well, just giving you my reasoning for not putting it in.
quicknir··on Joint Allocations in C++
Post author here. Sorry you feel that way. I'm surprised you don't think that moving out the messy code into make_contiguous is a win. Isn't it good to factor out clearly reusable code into functions, so you can reuse it? Would you really prefer writing out code like that in a dozen places for a dozen classes that needed contiguous storage, to writing one function, testing it, and then calling it from all those other places?
quicknir··on Joint Allocations in C++
I don't know as I'm not a game developer, but my guess is that individual Meshes may correspond to things that need to be created and destructed on a regular basis during runtime. If that's the case, then depending on the details it may be preferable to have an array of Meshes, and not try to pool all the Meshes together.

I think it's risky to say that it's misguided without the full context of the problem.

quicknir··on Object Theft Etiquette in C++: methods with a side of &&
So, in retrospect, my example was not a good one, I'm afraid. the recomputing of the string was a red herring. All that matters is that the moved-from object was in a valid state. You can look at the updated post, maybe it will help a bit.

If you want a concrete example, consider std::stringstream. You agree this class is useful, right? In many cases, you build up a string, bit by bit. You then want to extract the string at the end. Because stringstream was written before move semantics, that string is returned by value. That means a full copy of the internal string buffer is made.

In 95% of real life use cases of stringstream, the stringstream is a local variable used to build up a string and then discarded. So copying that string is fundamentally a waste. But exposing the string buffer in a way that std::move could be fruitfully called on it is dangerous, it may break stringstream's class invariants and turn it into a toxic object, this is avoided in C++. So the preferred solution would be to give stringstream::str an rvalue overload, so that the stringstream can safely release its string buffer. Does that make sense?

Of course I am aware that this code involves extra work, is harder for less experienced team members to understand, etc. I wouldn't do it in the vast majority of situations. But sometimes it is useful. The goal was to try to help people understand &&/& member overloads, as there isn't much discussion of them.

Respectfully, I am a professional c++ developer. Odds are that the code I write is under greater pressure for performance and genericity. Maybe a bit of benefit of the doubt is in order.

quicknir··on Object Theft Etiquette in C++: methods with a side of &&
Dude, the stealing etc was clearly tongue in cheek.

It seems like the example I picked was a bit unfortunate in the following sense: a couple of people have already misinterpreted the point of the article to be that foo still had content after I moved its string (I won't use stole anymore, seems like a touchy subject).

That's not the point, the point is that the invariants are maintained, which is the requirement for a valid state. I could have reset m_set at the same time as I set m_cached to false, it would still be just as correct.

Edit: I have modified the post slightly so that it's a bit clearer that what I'm after is a valid state for foo, not to maintain the specific things that were inserted.

quicknir··on Object Theft Etiquette in C++: methods with a side of &&
Actually, that's not true. Here's what I wrote:

"By stealing the string, we put foo in an invalid state".

foo is an object of the SetStringer class. It puts it an in invalid state because it no longer maintains its own invariants.

The whole point of this article is about how you can move member variables for efficiency, while ensuring the owning object is left in a valid state (as move/rvalue semantics require).

Wanting to be able to move a member variable is no stranger than wanting to move the object itself; in that sense an rvalue ref overloaded getter is no stranger than move construction/assignment, it's just more rare because it's less used.

quicknir··on Object Theft Etiquette in C++: methods with a side of &&
Thanks for the catch, fixed.
quicknir··on C++'s Rule of Zero
Yes, of course you can do that, but you lose certain things. In C++ you can write code (e.g constructors) that will do the right thing with their arguments, move or copy, based on whether the input is an rvalue or not. Is there a way to get this behavior in rust?
quicknir··on C++'s Rule of Zero
Right, which is great, don't get me wrong. If you're going to have that behavior, having the compiler enforce it is good.

I just think it's a bit amusing, people complain about this sort of thing in C++ all the time. I can imagine if Rust takes over people will complain how you have no idea what u = v is doing unless you know what u and v are.

I'll admit my biases, but I prefer the c++ way: types know how to both move and copy themselves, and they will copy by default, but move when it's safe (rvalue) or explicitly asked to (std::move). I like looking at auto u = v and knowing that u is always a copy of v.

quicknir··on C++'s Rule of Zero
It actually works quite well. It's not without its quirks, but unique_ptr is really great. If you think there are holes in unique_ptr, then write an article with some good examples, I'll be the first to read it.

Rust hasn't seen enough widespread use to find all the problems and limitations with the language. The borrow checker seems great, sure. But looking at how Rust handles moves vs copies, I'm not convinced at all that it will be good enough in the long run.

This "mess" is caused by imbuing Example with correct move and copy semantics. In rust, it seems like most types have one or the other, but not both. If a type does not implement Copy, then when you try to copy it, it automatically gets moved from. So u = v would change the value of v. Ironically, that's similar to the behavior of auto_ptr that you just criticized, and quite unintuitive.

quicknir··on C++'s Rule of Zero
Full disclosure: I'm the blog author.

If you're claiming that the verbose way is just writing out the special functions, then I can assure you that it is not easier to maintain. If you are saying that the class in question could simply write a nested class that wrapped unique_ptr and had the correct copying behavior, maybe, there are arguments for both.

This example is fully intended to be solid code for production use, not primarily to be "clever".

← PreviousPage 3 of 3