Live data from Hacker News

Orthodox C++ (2016)

bkaradzic.github.io

151–160 of 238 posts

Re: Orthodox C++ (2016)

#151
post #8

Earlier quoted context omitted.

One thing I've noticed about a lot of these "strict C" developers is that quite often they actually refuse to learn C++. One of the most common complaints of C developers regarding C++ is "it does things behind the scenes/performs magic", often with regards to operator overloading. When they refuse to actually look at the implementation (y'know you can check if an operator has been overloaded) AND they refuse to ackn…

I don't think "refusing to learn C++" is the right way to frame it. I want to use the language features that are actually useful to me, without being forced into a specific programming style. I can't speak for every "orthodox C++" programmer, but for me that means using exclusively plain-old-data structs, non-member functions, and "dumb" pointers. I have no issue with learning to use a C++ feature when it's directly…

Right. Use/Choose those features you are most knowledgeable/comfortable with. That is why C++ has such a smorgasbord of features and supports multiple paradigms. Over time, as one learns "better ways" (for a certain definition and one is convinced of it) of doing something change/modify as needed.

That is all there is to it.

Re: Orthodox C++ (2016)

#152

Earlier quoted context omitted.

> Exceptions aren't meant to report errors, just in general. That's a misuse of them. Exceptions are meant to be thrown when a contract cannot be fulfilled. Yes, you're unable to know what exceptions a function may throw. That's the way it should be, because exceptions aren't supposed to be part of the function's contract. I don't think these are true? What about std::vector::at(), std::optional::value(), etc.? And t…

>std::vector::at(), std::optional::value() Both functions must return T &. If the vector is not long enough, or the object is not set, then returning a T & is impossible. So we have a function that has already been called and which must return something valid, and cannot return something valid. The only two ways to resolve this contradiction is to throw, or to terminate. (Well, you could also trigger undefined behavi…

> The only two ways to resolve this contradiction is to throw, or to terminate.

Or they’re bad APIs that should be redesigned to be not bad.

They’re fallible functions. Don’t write fallible APIs that require exceptions to report errors! That’s bad API design!

Re: Orthodox C++ (2016)

#153

Earlier quoted context omitted.

Exceptions aren't meant to report errors, just in general. That's a misuse of them. Exceptions are meant to be thrown when a contract cannot be fulfilled. Yes, you're unable to know what exceptions a function may throw. That's the way it should be, because exceptions aren't supposed to be part of the function's contract. For example, you're implementing an arithmetic operator and have reached an erroneous state, but…

> Throwing an exception and letting it unwind the stack way up (perhaps even all the way up to main()) is the sensible solution No. I would never in a million years do this. If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result. Saying throw an exception an…

>If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result.

What if you don't control those call sites?

>Might as well just call std::abort.

Sure. I mean, not really, because the caller cannot handle an abort. You're making a decision for the caller that the situation is unresolvable, where the caller might disagree.

>No. You’ve simply made a new contract and the new contract is that under certain cases an error is returned in the form of an exception.

If the function doesn't use exceptions for normal error conditions, then no, it's not a new contract, because you don't need to do or know anything specific to handle the situation. You could do something like

  void transaction(){
    try{
      //...
      commit();
    }catch (std::exception &){
      rollback();
    }
  }
and not have to worry about the specifics. It's just an exception. You don't have to care about what exactly happened, you just care that something that couldn't be resolved happened. When exceptions are misused you see stuff like

  try{
    some_function();
  }catch (SomeErrorConditionSpecificToSomeFunction &){
    //...
  }
Not always, but this does usually mean that the exception is part of the contract of the function. It's a condition that the caller must handle as part of the normal usage of the function. FileNotFound exceptions are quite often a prime example of exception abuse.

Replying to your other comment here:

>They’re fallible functions. Don’t write fallible APIs that require exceptions to report errors! That’s bad API design!

I disagree. You should ensure your arguments are valid before indexing vectors and dereferencing optionals. You wouldn't iterate a vector like this, I imagine?

  for (size_t i = 0;; i++){
    auto x = vector.at(i);
    if (!x.has_value())
      break;
    //...
  }

Re: Orthodox C++ (2016)

#154

Earlier quoted context omitted.

fair, but it generates a std::string .. if you want to see it, what are you going to do with it? use .c_str() ??

If you want to see it where? If you want a string you've got a string. If you want to write text to something resembling file I/O (e.g. stdout) then std::print and std::println are what you need instead of std::format Stroustrup's I/O Streams is a weird dead-end C++ technology. Bjarne is probably never going to get over it, but everybody else should forget about it ASAP.

Worth pointing out that the iostreams library is the work of many hands, so Stroustrup is not solely to blame. I just found this old SO question which I answered many years ago (I am anon/Neil) which addresses this:

https://stackoverflow.com/questions/2753060/who-architected-...

Re: Orthodox C++ (2016)

#155
post #7

I've developed a style that I legitimately call Heterodox C++ (mainly due to the popularity of Orthodox C++), it is effectively a purely functional & metaprogramming heavy style of C++. Quite the opposite of this, not everyones cup of tea, and it won't fit into every codebase but it is incredibly powerful. The template metaprogramming C++ offers is the most powerful of any imperative language, and (subjective opinion…

Yuck. I'm sure your compile times suck and code is not readable (my subjective opinion as well). There's nothing worse than a ten-mile long lambda in C++. To each there own, however. C++ is four languages in one after all. :)

Templates are really amazing, once you learn the patterns there's basically a template version and a runtime version of things (policy types, etc.). All of that is great.

Legibility and understanding are in my opinion the most important aspect of any programming language, so Orthodox C++ is superior for maintainable code.

Re: Orthodox C++ (2016)

#156

Earlier quoted context omitted.

> Exceptions aren't meant to report errors, just in general. That's a misuse of them. Exceptions are meant to be thrown when a contract cannot be fulfilled. Yes, you're unable to know what exceptions a function may throw. That's the way it should be, because exceptions aren't supposed to be part of the function's contract. I don't think these are true? What about std::vector::at(), std::optional::value(), etc.? And t…

>std::vector::at(), std::optional::value() Both functions must return T &. If the vector is not long enough, or the object is not set, then returning a T & is impossible. So we have a function that has already been called and which must return something valid, and cannot return something valid. The only two ways to resolve this contradiction is to throw, or to terminate. (Well, you could also trigger undefined behavi…

I think you missed my point. I was referring to the fact that some of these standard exceptions are very much a part of the contracts of their respective functions. In fact, that's their entire point. This directly contradicts what you wrote.

Re: Orthodox C++ (2016)

#157

Earlier quoted context omitted.

> Throwing an exception and letting it unwind the stack way up (perhaps even all the way up to main()) is the sensible solution No. I would never in a million years do this. If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result. Saying throw an exception an…

>If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result. What if you don't control those call sites? >Might as well just call std::abort. Sure. I mean, not really, because the caller cannot handle an abort. You're making a decision for the caller that the si…

> What if you don't control those call sites?

If I am choosing to change the API contract then someone who wants to use the new API has to update. This is not a big deal.

> If the function doesn't use exceptions for normal error conditions, then no, it's not a new contract

I disrespectfully and emphatically disagree. I do not accept your definition of contract.

> You could do something like (try-catch wrapper)

Let me be clear. Having to add a bunch of random fucking try-catch bullshit around every fucking function call is EXACTLY why I hate exceptions and is EXACTLY what I think is bad software design.

If you think a function should return a value or some unspecified exception whose details are irrelevant then that function could return an option with no information loss, or a result with an Error that is ignored.

> You wouldn't iterate a vector like this, I imagine?

I wouldn’t use at(i) for iteration. The only reason for a function like at(i) to exist is because you want it to be fallible.

Re: Orthodox C++ (2016)

#158
post #108

Earlier quoted context omitted.

> The template metaprogramming C++ offers is the most powerful of any imperative language I'm curious what languages you're comparing to here. Feels like it's only slightly more expressive than pure generics, but I admittedly haven't done much template metaprogramming myself. How does it compare to, say, Zig's comptime?

I’ll preface this by saying I mostly use C++ and have just basic experience in Zig, but as far as I understand comptime is much more procedural than C++ templates which are more declarative. With templates you get quite good pattern matching through the compiler’s machinery, for instance through template specialisation, while Zig, in my understanding, requires these to be handled manually in code. Personally, comptim…

> The significant downside of C++ metaprogramming is that sometimes many features interact weakly and feel very much tacked on top of each other while Zig’s looks more cohesive.

spot on, in my experience. nothing in zig triggers my lite OCD.

Re: Orthodox C++ (2016)

#159

Earlier quoted context omitted.

>std::vector::at(), std::optional::value() Both functions must return T &. If the vector is not long enough, or the object is not set, then returning a T & is impossible. So we have a function that has already been called and which must return something valid, and cannot return something valid. The only two ways to resolve this contradiction is to throw, or to terminate. (Well, you could also trigger undefined behavi…

I think you missed my point. I was referring to the fact that some of these standard exceptions are very much a part of the contracts of their respective functions. In fact, that's their entire point. This directly contradicts what you wrote.

You're using "contract" in a different sense than I did. When I said "contract" I was referring to the required state of the program when the function is called and the guaranteed state of the program when the function returns. By definition an exception cannot be part of the contract in this sense, because a call that throws does not return. This narrower sense of contract is critical, because the entire point of exceptions is to enable alternate control path when it'd otherwise be impossible, such as in the examples I gave above with overloaded operators and code with evolving requirements.

Re: Orthodox C++ (2016)

#160

Earlier quoted context omitted.

>If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result. What if you don't control those call sites? >Might as well just call std::abort. Sure. I mean, not really, because the caller cannot handle an abort. You're making a decision for the caller that the si…

> What if you don't control those call sites? If I am choosing to change the API contract then someone who wants to use the new API has to update. This is not a big deal. > If the function doesn't use exceptions for normal error conditions, then no, it's not a new contract I disrespectfully and emphatically disagree. I do not accept your definition of contract. > You could do something like (try-catch wrapper) Let me…

> having to add a bunch of random fucking try-catch bullshit around every function call

Not the person you are replying to, but did you see where he said:

> When exceptions are misused

Post reply on HN