5 ms·
Nope, if you have an operation that can fail then wrap it in a type that represents that(I'm partial to Rust's Result<T,E>) then you force consumers to deal wit
by vvanders 10y ago
Nope, if you have an operation that can fail then wrap it in a type that represents that(I'm partial to Rust's Result<T,E>) then you force consumers to deal with potential errors and you can do 1-call construct + init if you want to.
Exceptions cause all sorts of problems(like violating the principal of only paying for what you use). They aren't checked so you can't enforce handling via type system and in general they're just a mess in C++.
- Arnavion 10y agoC++ constructors can't return error codes or Result. That's the two-stage initialization (constructor call + initialize() call) wvenable is talking about. (Of course you could do the same thing as Rust and expose only a static creator function and hide the actual constructor.)
- vvanders 10y agoYeah, most projects I worked on was doing the static creator before Rust showed up on the scene since it sidesteps a lot of these issues.
- int_19h 10y ago> Exceptions cause all sorts of problems(like violating the principal of only paying for what you use). Exceptions are zero-cost in terms of performance on any sane architecture (this excludes x86, but not x64). The only cost that you're paying is the extra memory for the tables, which are all statically allocated.
- vvanders 10y agoDon't you need RTTI as well? That's not been zero-cost on all compilers I've used in the past.
- int_19h 10y agoYou do, but C++ includes RTTI separately from exceptions (dynamic_cast and typeid), so it's going to be there regardless. If you turn it off, then we're not talking about standard C++ anymore, but some subset.
- wvenable 10y agoWhen you throw and exception, C++ compilers add RTTI for those classes even when RTTI is separately disabled. You cannot call dynamic_cast and type_id but the generated code is the same. I don't really see this as much of an issue given you have to have this RTTI to catch the exception properly.
- int_19h 10y agoMy point is that RTTI is required by other things in the C++ standard, not just exceptions. So it's still "free" if we're only considering exceptions separately by themselves, and leave all other features as they are in standard C++ (and if you disable RTTI entirely, then you no longer have standard C++, since those other features also stop working). If we consider the combo of exceptions+RTTI, then yes, dropping both at the same time lets you get rid of some further overhead (although again it's memory-only, not compute cycles). But then we have to discuss the lost benefits of those other features, not just exceptions alone, to judge whether doing so is worthwhile or not. BTW, I may well be wrong here, but isn't RTTI generation for exceptions (when it's otherwise disabled) an example of "only pay for what you use"? i.e. wouldn't the compiler then only generate RTTI entries for those types that are either thrown somewhere, or are caught somewhere? If so, any library code that doesn't throw itself, and only needs to allow exceptions to pass through it, is still zero-overhead in that regard.
- dbaupp 10y agoThey're zero(ish) cost at runtime, but focusing on just that is missing something: pervasive unchecked exceptions inhibit optimisations, both in compilers and in libraries, which have to be more defensive to preserve correctness.
- searealist 10y agoI guess that's why you Rust guys don't have exceptions, so you don't have to pay this cost. </s>
- dbaupp 10y ago(Explanation of the /s: panics use the same mechanism, so the compiler and libraries unfortunately still have to be just as defensive when they're enabled. When the compiler is in -C panic=abort mode, it doesn't suffer from this, but I don't think libraries have a great way to customise/improve their behaviour in that case at the moment.)
- int_19h 10y agoIf you replace exceptions with e.g. return values, it doesn't really change anything for the libraries - if they expect to be able to deal with an error, they still have to defensively preserve correctness. OTOH the library always has the option to say "this particular thing that you give me should be nothrow; if it throws, that's U.B.", and not even try being defensive in the face of exceptions when they come out of such a thing.
- milesrout 10y agoThe thing is, in C++ you have to explicitly write noexcept for that to happen. If you use optional<T> you have to explicitly write that, and turning off exceptions means you don't have to write noexcept everywhere.
- dbaupp 10y agoI think you missed the precise qualification of my comment: this only applies when the compiler can't tell what exceptions can occur, like in C++, Rust and even Java, and so needs to be able to handle any/all of them. The most basic (and most relevant) query is "can this call throw?", but even that is impossible to answer without global information when there are unchecked exceptions. In statically typed languages, return values, like (fully) checked exceptions, are different, as they're exactly the sort of global information the compiler needs. Functions can statically guarantee the exact set of ways in which it can interact with its caller, and the compiler can understand. This means a function that, say, sorts a list with a less-than function (that returns bool) knows that every time that function exits, it gives a value of true or false, no exceptions (hah). In any case, just writing a library that says "UB to throw" isn't quite enough, as the compiler won't understand, and will still structure code to handle exceptions "sensibly", even thought it doesn't need to. Fixing that means being able to communicate nothrow to the compiler (which is nice too), but this starts to no longer be a pervasive unchecked error system: it's somewhat of a binary form of checking.
- muizelaar 10y agoCompiling with exceptions can still have performance cost because the addition of implicit control flow at every function call. For example the following code will compile differently depending on whether goo() can throw. struct k { void p[4]; }; void goo(k s); k foo() { k r; k s; goo(&s); r = s; return r; } If goo() doesn't throw the extra variable s can be eliminated.
- aristidb 10y agoTry indenting your code section by 4 spaces. :)
- int_19h 10y agoIf goo doesn't throw, why not declare it nothrow? Although I still don't see why, in your example, one of the variables cannot be elided even if goo does throw. Since there's no point at which address of r is taken, much less escapes the scope, it should always be safe to elide r and just return s, no?
- deleted 10y ago[deleted]
- flamedoge 10y agoyou mean r? you can't eliminate s regardless of goo throwing.
- easytiger 10y ago> Exceptions are zero-cost in terms of performance on any sane architecture Not when the exception is triggered
- int_19h 10y agoIf you trigger an exception, then you're not paying for something that you don't use anymore.
- saghm 10y agoIn terms of memory usage for similar Result type in C++, is there any way around allocating the space for both types (as opposed to Result in Rust, which just allocates enough space for the larger of the two)? My understanding is that there is not, although I'm still not very experienced with C++, so I wouldn't be surprised if I'm wrong
- vvanders 10y agoYou can use a union + tag(which the same as Rust uses which is why they're called Tagged Unions) however you run into issues with constructors/destructors. You can do something similar with placement new/delete but it requires some hoops. Honestly most error codes are small enough that you can just pay the hit for it on the stack and discard it once you've handled the good/bad case.
- gpm 10y agostd::variant with c++17, but on linux at least you need to install gcc from git to have access to it so far. boost::variant otherwise, but depending on the types used (if there aren't any that are nothrow default constructible) it allocates on assignment. If you just want a result type and not a more general "rust enum", this repository does it (while only allocating space for the larger, I think) https://github.com/oktal/result/ https://github.com/oktal/result/ And I don't think there's any reason you couldn't implement an equivalent of std::variant for previous versions of C++, I'm just not aware of any.
- irishcoffee 10y agoHeh, I implemented an in-house variant at work a week ago. As it turns out, I was able to use my variant class in a python extension I developed. Also, as it turns out, python is scary in what it lets you get away with when developing extensions.
- petters 10y agoExceptions are typically faster than error codes when errors are rare.