$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [review] Review of Outcome v2 (Fri-19-Jan to Sun-28-Jan, 2018)
From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2018-02-01 01:58:13
On Wed, Jan 31, 2018 at 4:52 PM, VinÃcius dos Santos Oliveira <
vini.ipsmaker_at_[hidden]> wrote:
> 2018-01-31 20:25 GMT-03:00 Emil Dotchevski <emildotchevski_at_[hidden]>:
>
>> Where is the compile time error? Example?
>>
>
> Check the first Outcome tutorials.
>
> This won't compile:
>
> std::cout << (convert(text) / 2) << std::endl;
>
> This will:
>
> OUTCOME_TRY(foo, convert(text));
> std::cout << (foo / 2) << std::endl;
>
Does this guarantee that your program is dealing with the error correctly?
Not at all, if the context within which you call convert is not
exception-safe, you'll get the same leak you'd get had you used exceptions.
The difference is that you'd also get a compile error if you forget to
check for errors, which a C++ programmer can't forget to do in this case.
This is similar to how you have to deal with object initialization if you
don't use exceptions. Compare:
class foo
{
bool init_called_;
public:
foo(): init_called_(false) { }
result<void> init()
{
....
init_called_=true;
}
result<int> foo()
{
assert(init_called_);
return compute_int();
}
result<void> bar( int x )
{
assert(init_called_);
//use x
}
};
....
foo a;
OUTCOME_TRY(a.init());
OUTCOME_TRY(x,a.foo());
OUTCOME_TRY(a.bar(x));
Now the C++ version:
class foo
{
foo()
{
//initialize, throw on errors
}
int foo()
{
return compute_int();
}
void bar( int x )
{
//use x
}
}
....
foo a;
a.bar(a.foo());
Note, I am not only making the point that the Outcome version is more prone
to errors and more verbose, I am saying that even if you make no logic
errors writing it, the result is a program that is _semantically_ identical
to the one a C++ programmer would write using exceptions, complete with the
bugs that could creep in if your code is not exception-safe.
Literally, there is no upside to that style of programming.
> You can't forget to handle the error case. You can look at a function and
> you'll immediately know if this function can fail or not.
>
In C++, we use noexcept to mark functions that can not fail.
> You can't draw conclusions only by looking at anectodal evidence like
>> this. If you want to demonstrate that Rust-style error handling is more
>> robust, you have to have a control, the same real-world large scale project
>> written/designed for using C++ exception handling. Neither of us has a
>> control.
>>
>
> At least you dropped the "axiomatic belief". But I haven't relied on
> anecdotal evidence. I specifically told you to show me /any/ code on Rust
> that is as complex as the examples I gave.
>
Yes, I understand that you like Rust and dislike C++. :)
I am well aware of the difficulties in writing C++ code. Like I said, with
some exceptions, there is usually a good reason for that.
> C++ allows complexity to just snowball and it is not a opt-in feature.
> It's always there. Therefore, you always maintain useless state in your
> head when analysing C++ code. Go ask C programmers what they think about
> C++ exceptions.
>
You're making my point, that C++ exception handling is not an opt-in
feature, it is inseparable part of the language, central to its object
encapsulation model, etc. etc. I know what C programmers think about C++
and about exceptions in particular. Do note that they don't propose C
libraries to be added to Boost or C++, they don't want to touch either.
By the way, I have a lot of respect for C and for people who choose it over
C++. I've also learned the value of using C-style interfaces between
modules in a large scale C++ projects.
> So you have to be able to make your point in the abstract, which is very
>> difficult. It is especially difficult because using either approach, a high
>> quality development team can produce a robust program. But just because
>> Google managed to build a phone using Java, it doesn't mean it was a good
>> choice.
>>
>
> You still ignore the fact that I'm talking about a qualitative (not
> quantitative) property: more errors go to compile time errors. I have been
> focusing on such aspect since the beginning of the discussion.
>
I demonstrated why this is false, but there is no such thing as free lunch.
Everything has cost. It is important to keep this in mind when criticising
C and C++.
> It's fact that you can't ignore the error because the code will fail to
> compile. Error cases are explicit.
>
> Yes, using exception handling you can make mistakes that would be less
>> likely to make otherwise. Also, using C++ you can make a whole lot of
>> mistakes you can't make in Java, that's one reason why some people use Java
>> and not C++, but this doesn't make them good C++ programmers -- nor it
>> means that there is something wrong with C++.
>>
>
> Agreed.
>
> There are very many examples of things being very difficult to express in
>> C++ while being trivial in another language. There are usually good reasons
>> for this, and there are many examples to the contrary. The ability to use
>> RAII and exceptions to enforce postconditions is one such advantage.
>>
>
> Not sure what kind of postcondition you're talking about this time.
>
What postcondition, that depends on the design, but the effect is that
you're making it impossible for control to reach contexts for which it
would be a logic error to execute in case a previous operation failed.
> If it is class invariants, you can have them without exceptions.
>
You can have them, but you can't know if they're been established because
in case of a failure, control may still reach code that requires them to be
in place.
> And just one note about RAII: Rust does have RAII. No GC. A lot about the
> language was inspired by C++ knowledge.
>
Yes, I get that you love Rust. :)
> I'll point out again that in C++ it is impossible to report an error from
>> a constructor except by throwing, and this is a good thing. I assume you
>> disagree -- so what do you propose instead? It can't be OUTCOME_TRY, can it?
>>
>
> I'm not proposing you to code differently.
>
My question still stands though. If you don't use exceptions, in C++, how
do you protect users from calling member functions on objects that have not
been initialized?
Emil