4 ms·
And then every object has one more state.
by jnwatson 5y ago
And then every object has one more state.
- Thorrez 5y agoYou mean the factory function has to return some invalid state of the class? Not so. The factory could return std::optional<Foo> or std::unique_ptr<Foo> or std::variant<Foo, Error> or absl::StatusOr<Foo> .
- jcelerier 5y agoYour program will still have as many possible memory states as if you had a bool m_isValid; in your type - as now you don't store a Foo but an optional<Foo>. It's an improvement in terms of code reuse but not in semantic simplification basically.
- pwdisswordfish8 5y agoYou realise you can just unwrap an optional after checking it’s not nullopt, right?
- jcelerier 5y agoNot if it's part of the state of your app ? E.g. a struct member. It also replaces an automated and enforced action by the compiler (exception being thrown) by a need for a manual check which feels very 1975
- Thorrez 5y ago>Not if it's part of the state of your app ? E.g. a struct member. I'm not sure what you mean. Here's an example with the output from std::optional being unwrapped and put in a struct member: https://godbolt.org/z/Ms4Kar18j https://godbolt.org/z/Ms4Kar18j Also there's opt.value_or() which can use a default value in the case opt is nullopt. >It also replaces an automated and enforced action by the compiler (exception being thrown) by a need for a manual check which feels very 1975 I think remram's goal was to avoid exceptions. So this is a criticism of remram's goal rather than the method to achieve that goal. Also, std::optional can integrate with exception-producing code because opt.value() throws an exception if opt is nullopt.
- jcelerier 5y ago> I'm not sure what you mean. Here's an example with the output from std::optional being unwrapped and put in a struct member: https://godbolt.org/z/Ms4Kar18j https://godbolt.org/z/Ms4Kar18j And now you have to do this for every type that have some amount of preconditions. I don't understand in which universe this is sane - more code == more bugs. e.g. here's what one would write with exceptions: https://godbolt.org/z/evMsh6383 https://godbolt.org/z/evMsh6383 (and, to be honest, if one is writing e.g. a command-line tool and not a reactive gui app, most likely https://godbolt.org/z/eW8YM5Gz4 https://godbolt.org/z/eW8YM5Gz4 as your OS will catch the thing anyways and then it's just a `coredumpctl gdb` / "open in debugger" away) ; you get the same guarantee of never having an invalid Foo but at much less mental cost. It also prevents you of having aggregates as all the "parent" owner class will need their own wrapping in optional + private ctor, in case a sub-sub-sub-sub field would fail. e.g. given struct DomainObject { Foo mainFoo; Foo secondaryFoo; int whatever; }; struct GameState { std::vector<DomainObject> objects; }; now you can't just do DomainObject{initForMainFoo, initForSecondaryFoo, 123}; or DomainObject{.mainFoo = "", .secondaryFoo = "", .whatever = 123}; anymore, even less GameState{{DomainObject{...}, DomainObject{...}}; which is how modern C++ is meant to be used
- pkolaczk 5y agoException handling may indeed end up with less code, but often it is because the developers forgot to handle most error situations properly. And that's my biggest gripe with exceptions, particularly the unchecked ones, which can just pop up from 100 layers below. I've just seen it too many times when developers just throw exceptions and let them bubble up to a place where there is so little context that the final error message presented to the user is near useless. On the other hand return values (using something like a try/either monad) have the property of forcing the developer to think about the unhappy path in every layer. Maybe a bit more code, but the quality of diagnostics - priceless. IMHO exceptions/panics should be reserved for bugs only (eg assertions), and return values for all the other error handling caused by user input or environment.
- jcelerier 5y ago
- rualca 5y ago> And then every object has one more state. No, not really. Either the allocation followed the happy path, or one of the expected failure modes kicked in. And the supposed footgun example boiled down to failing to handle one of the failure modes. I mean, think about it: std::make_shared is a factory method, isn't it?