Live data from Hacker News

Orthodox C++ (2016)

bkaradzic.github.io

201–210 of 238 posts

Re: Orthodox C++ (2016)

#201

Earlier quoted context omitted.

Funny this predates even the original paper first introducing the attention mechanism underpinning modern LLM.

What are you talking about? Are you claiming ~two weeks ago predates LLMs or am I misinterpreting what you're trying to say?

You're talking past each other, about different articles.

Re: Orthodox C++ (2016)

#202

Earlier quoted context omitted.

You are free to make your own definition, what are your suggestions? (Obviously I meant to say low latency, not low frequency)

That is a cop-out. You made the assertion, you define it. What about these particular workloads (and the environments they're used in) make them 'serious' and why are other workloads 'lesser' and therefore the standard library 'suffices'? Why not use better containers for everything? Google, for instance, universally recommends Abseil.

I stand by my statement. You are welcome to contend it, but then please actually suggest alternative workloads that could qualify as serious?

Google also recommends using Golang in many cases, which was explicitly designed for not-so-experienced people. It is more geared towards creating services quickly and robustly, not towards squeezing out the last 10x of performance.

I can't say anything about abseil, having never used it. "By Google" is not an immediate seal of quality, and it doesn't mean that one size fits all. I've recently seen a list of .so objects required in order link grpc (also by Google), there were like dozens of abseil .so's in the list IIRC. I don't like it! In my experience, validated countless times in my own practice, complexity => slow and hard to maintain and simplicity => fast and easy to change.

Templatized C++ containers are generic recipes, they can't make use of local context and hence don't lead to the simplest solutions. They produce tons of boilerplate. They give you a decent single-threaded baseline performance in many cases, sure, but now start hammering these data structures using 16 CPUs in parallel... you might find that you should have designed your application completely differently.

Like WalterBright says, I too use very simple data structures almost exclusively (arrays, linked lists, linear allocation). Most of these coded ad-hoc everywhere, very straightforward, very adaptable. Even hash maps I only use infrequently -- if I can, I just make keys such that I can index directly. I get performance from knowing exactly what happens, and minimize the work that needs to happen. I create immutable data objects (write-once) where possible. I've created my own pooled block-allocator for power of 2 sized allocations with book-keeping in shadow-memory (preventing fragmentation), massively reducing syscalls that modify virtual memory mappings and closely controlling memory used by various subsystems.

I don't expect a library like abseil to solve many of my problems, it probably makes some things "easy" by taking away the very control from me that I need to solve the problem I have, resulting in unsolved problems and complexity.

Re: Orthodox C++ (2016)

#203

Earlier quoted context omitted.

>But you do have to think about it in the sense that every single line in your code could unwind. No, this is actually just wrong. There is code that can throw, and there is code that cannot possibly throw. The way you write exception-safe code is by not holding manually-managed resources (e.g. raw pointers that own heap allocations, or file descriptors that must be close()d, or anything else that needs cleanup code…

Program state is significantly more complex than just needing some RAII resources to cleanup via destructors. > during sections that may throw Yeah one of the problems with exceptions is it’s impossible to know what “may throw” other than “well I guess literally anything so everything”. It is very irritating. At the end of the day exceptions are just a little syntactic sugar. Or perhaps syntactic bitters. It is notab…

>Program state is significantly more complex than just needing some RAII resources to cleanup via destructors.

You're being rather vague. All throwing does is cause control flow to jump to the nearest catch that can handle the exception, destructing all objects along the way. I struggle to think of an example that could cause problems that isn't some variation of "I had some code after the exception that I needed to run, and it didn't run, because it wasn't set up to run at scope exit". I'd love to see such an example if you have one.

>it’s impossible to know what “may throw”

* If it's a throw statement, it may throw.

* If it's an expression that contains a 'new' operator, it may throw.

* If it's an expression that contains a dynamic_cast to a reference type, it may throw.

* If it calls a function that you don't know that it does not do any of the above, it may throw.

* If it's unknown if a function is called (e.g. types are templated), it may throw.

* Otherwise, it doesn't throw.

If you're managing resources manually, either make sure not to call any functions until you release them, or stop managing them manually. I encourage the latter.

Re: Orthodox C++ (2016)

#204

Earlier quoted context omitted.

What are you talking about? Are you claiming ~two weeks ago predates LLMs or am I misinterpreting what you're trying to say?

You're talking past each other, about different articles.

Yeah, that makes sense. Perhaps the downvoters also didn't realise that I was talking about the 'HFT University' article.

Re: Orthodox C++ (2016)

#205

Earlier quoted context omitted.

Vec::reserve() is the behaviour you get from C++ std::vector push_back() (implicit just-in-time reserve), so right now I can't see a situation where I'd want the explicit Rust version even if I didn't think the whole push realloc thing is mostly a bad idea. Yes, Rust version allows you to maybe skip a reallocation step or two by doing explicit up front reallocation. But remember most allocation work is always from th…

As with the likely/ unlikely branch hint the problem is that programmers are wrong much more often than they expect on both sides . They're too often wrong to think they know the final size - hence Bjarne's caution - but they're also too often wrong that they've got no idea how much capacity they need at all. So hence this API. You're correct that this isn't a huge optimization. But it more than pulls its weight dire…

There are many many situations where I know exactly what is the common path and what is the uncommon path. And when I don't know, it's much harder to optimize!

If you're counting pennies, Vec::reserve() (inexact) is hardly what you want, because in the worst case you're wasting a factor of 1.5x or 2x of elements due up-front overallocation. Maybe chunk lists could be better, overhead is bounded by chunk size and all operations are constant-time. No pointer invalidation either. And you can pool those chunks, preventing memory fragmentation and improving memory utilization, since there aren't a million different sized allocations in your process.

Re: Orthodox C++ (2016)

#206

Earlier quoted context omitted.

Program state is significantly more complex than just needing some RAII resources to cleanup via destructors. > during sections that may throw Yeah one of the problems with exceptions is it’s impossible to know what “may throw” other than “well I guess literally anything so everything”. It is very irritating. At the end of the day exceptions are just a little syntactic sugar. Or perhaps syntactic bitters. It is notab…

>Program state is significantly more complex than just needing some RAII resources to cleanup via destructors. You're being rather vague. All throwing does is cause control flow to jump to the nearest catch that can handle the exception, destructing all objects along the way. I struggle to think of an example that could cause problems that isn't some variation of "I had some code after the exception that I needed to…

> You're being rather vague.

Completely forget about memory allocation and memory allocation like things.

Let’s say I have a physics system that runs an update. Assume we catch outside the update. If anything throws the system is now in an intermediate state and is effectively irrecoverable.

If you want to argue that exceptions should only be used for irrecoverable errors such that the subsystem is not expected to resume then I would list. This is akin to Rust panics which unwind like C++ exceptions but are not intended to resume. I have not read any comments in this large subthread arguing for using exceptions in this narrow style.

> it’s impossible to know what “may throw”

Yeah you’re just saying you should basically assume that any code can throw at any point. I think that’s a really really bad design pattern that makes code significantly harder to reason about. Control flow should be clear and obvious. “Any line of code could immediately unwind to unclear catch” is the objectively not clear and obvious.

But let me turn it around. You tell me an example of a system that doesn’t use exceptions where adding exceptions makes it better.

Re: Orthodox C++ (2016)

#207

Earlier quoted context omitted.

> If you crash from an unhandled exception, you don't. .. you absolutely get a stack trace from unhandled exception in c++, that starts where the exception is thrown? At least with clang and GCC, maybe MSVC isn't able to. foo.cpp: #include running: $ g++ foo.cpp -std=c++23 -g $ ./a.out terminate called after throwing an instance of 'std::runtime_error' what(): boo $ coredumpctl gdb ... #7 0x00005555555551bb in bar ()…

Sure, in a desktop program you do. In embedded exceptions are a scourge because they crash your program until someone can get back out there to power cycle it. At least with return-codes you can continue execution even if you failed to effect the change you wanted. If that was ancillary to the system’s core function then the system keeps running.

Pretty sure esp32 just rebooted automatically when unhandled exception is thrown for instance

Re: Orthodox C++ (2016)

#208

Earlier quoted context omitted.

>Program state is significantly more complex than just needing some RAII resources to cleanup via destructors. You're being rather vague. All throwing does is cause control flow to jump to the nearest catch that can handle the exception, destructing all objects along the way. I struggle to think of an example that could cause problems that isn't some variation of "I had some code after the exception that I needed to…

> You're being rather vague. Completely forget about memory allocation and memory allocation like things. Let’s say I have a physics system that runs an update. Assume we catch outside the update. If anything throws the system is now in an intermediate state and is effectively irrecoverable. If you want to argue that exceptions should only be used for irrecoverable errors such that the subsystem is not expected to re…

>Let’s say I have a physics system that runs an update. Assume we catch outside the update. If anything throws the system is now in an intermediate state and is effectively irrecoverable.

That's a transaction kind of scenario. You catch at a recoverable point and rollback to a good state, and if that's not possible, then you simply fail out. I don't understand; what problem did exceptions introduce here? An exception was thrown (i.e. an error happened) during an intermediate operation and the operation as a whole stopped. What would have changed if every function had used error codes instead? It would have been either an error you were incapable of handling (in the sense that you didn't even look at the error code), and the operation would have silently continued in a possibly corrupted state, or it would have been an error you were capable of handling, in which case implement that same handling logic for the exception code.

  if (op1() != SUCCESS){
    //recover and continue
  }
  if (op2() != SUCCESS){
    //nothing to do so just fail out
    return FAIL;
  }
  //don't care about error so don't even bother checking
  (void)op3();
  //now the state of the program may be invalid
  op4();
becomes

  try{
    op1();
  }catch (/*...*/){
    //recover and continue
  }
  op2(); //let exception be caught by someone who can do something about it
  op3(); //same
  //sometimes we won't get here, but at least if we do, we know the state of the program is valid
  op4();
Is it just that you like ifs more than try-catches?

>If you want to argue that exceptions should only be used for irrecoverable errors such that the subsystem is not expected to resume then I would list.

That would depend on what you mean by "irrecoverable error". I interpret that to mean that the program cannot continue to function safely and it's better to terminate ASAP than to attempt to do anything else at all. If that's what you mean then no, that's not what exceptions are for. Like I said in a sibling comment (https://news.ycombinator.com/item?id=48527216), exceptions are meant to signal an error that may or may not be recoverable that the immediate caller may not know how to handle. Someone in the call stack should be able to decide based on program state how to respond to the error condition; sometimes that will involve rolling back to a valid state, as I said before, perhaps retrying; sometimes you will cancel the operation and discard the interrupted computation; sometimes you will notify the user or log an error; sometimes you will decide that there's actually nothing else for the program to do and clean up and exit.

>You tell me an example of a system that doesn’t use exceptions where adding exceptions makes it better.

Sure, no problem:

  if (op1() != SUCCESS)
    return FAIL;
  if (op2() != SUCCESS)
    return FAIL;
  if (op3() != SUCCESS)
    return FAIL;
  //etc.
with exceptions becomes

  op1();
  op2();
  op3();
  //etc.
All else being equal, exceptions make this kind of code better by making it more readable. If the non-exception code needs to return both error codes and partial values, it becomes even more noticeable:

  auto [result1, error1] = op1();
  if (error1 != SUCCESS)
    return FAIL;
  auto [result2, error2] = op2(result1);
  if (error2 != SUCCESS)
    return FAIL;
  auto [result3, error3] = op3(result2);
versus:

  op3(op2(op1()))

Re: Orthodox C++ (2016)

#209

Earlier quoted context omitted.

> You're being rather vague. Completely forget about memory allocation and memory allocation like things. Let’s say I have a physics system that runs an update. Assume we catch outside the update. If anything throws the system is now in an intermediate state and is effectively irrecoverable. If you want to argue that exceptions should only be used for irrecoverable errors such that the subsystem is not expected to re…

>Let’s say I have a physics system that runs an update. Assume we catch outside the update. If anything throws the system is now in an intermediate state and is effectively irrecoverable. That's a transaction kind of scenario. You catch at a recoverable point and rollback to a good state, and if that's not possible, then you simply fail out. I don't understand; what problem did exceptions introduce here? An exception…

> All else being equal, exceptions make this kind of code better by making it more readable.

I just fundamentally disagree that hidden secret control flow makes code more readable. Well, it may be more readable but it is, imho, significantly less understandable.

There’s a reason that literally no modern systems language has adopted C++ exceptions.

We’re just about at the point of discussing syntactic sugar. So one question is “given current C++ capabilities should you use exceptions or not?”. Another is “should C++ add different sugar for error handling?”. And a third is “should a different language adopt exceptions-like design?”.

Imho a zig or rust like error system with a ? operator to return is vastly superior. Fallibility and control flow is super explicit, obvious, and easy to read.

Current C++ is a little jankier. Designs vary. All have tradeoffs. But imho they are all preferable to secret hidden control flow. So exceptions are, in my lived experience, a strictly inferior choice.

Re: Orthodox C++ (2016)

#210
post #127

Earlier quoted context omitted.

Unlike your comment, which is the pinnacle of human thought? You are also wrong.

I'm not wrong. It's been removed elsewhere for being LLM generated. This discussion has already been had multiple times. The author used Claude to generate it and instructed it to intentionally insert mistakes. They had comments posted on Reddit that discussed doing this and their account is a slew of AI generated responses. The short responses where it appears they didn't use AI don't reflect the behaviour you'd exp…

You could have elaborated the way you did here to begin with.

> shame on you if it's your website, given that you've previously submitted content from there

How do you jump to such a conclusion without evidence? Has somebody hurt your feelings on the Internet?

Post reply on HN