[Spice-devel] [PATCH 12/22] Add exception handling classes
Jonathon Jongsma
jjongsma at redhat.com
Wed Mar 7 14:14:44 UTC 2018
On Wed, 2018-03-07 at 12:34 +0100, Christophe de Dinechin wrote:
> > On 6 Mar 2018, at 17:47, Jonathon Jongsma <jjongsma at redhat.com>
> > wrote:
> >
> > On Tue, 2018-03-06 at 11:02 +0100, Christophe Fergeau wrote:
> > > On Fri, Mar 02, 2018 at 06:06:32PM +0100, Christophe de Dinechin
> > > wrote:
> > > > > FWIW, I also find myself in agreement with Frediano and Lukas
> > > > > on
> > > > > this point.
> > > >
> > > > OK. Maybe I misunderstood something.
> > > >
> > > > Do we agree that the case Frediano raised is someone trying:
> > > >
> > > > throw Error(“The value of “ + std::to_string(2) + “
> > > > does not
> > > > match “ + std::to_string(1));
> > > >
> > > > which yields the following error:
> > > >
> > > > spice-streaming-agent.cpp:165:93: error: no matching
> > > > function
> > > > for call to
> > > > ‘spice::streaming_agent::Error::Error(std::__cxx11::basic_strin
> > > > g<ch
> > > > ar>)’
> > > >
> > > > So far, I think the interface was not “error prone”. It caught
> > > > the
> > > > error correctly.
> > > >
> > > > Therefore, in order to find the interface “error prone”, one
> > > > has to
> > > > accept a second hypothesis, which is that whoever attempted
> > > > that,
> > > > after seeing that error message, looks at the interface, which
> > > > has
> > > > a ‘const char *’ and a comment that specifically states "It is
> > > > recommended to only use static C strings, since formatting of
> > > > any
> > > > dynamic argument is done by ‘format_message’”, and after
> > > > reading
> > > > that comment, has a big “Aha” moment and writes:
> > > >
> > > > throw Error((“The value of “ + std::to_string(2) + “
> > > > does not
> > > > match “ + std::to_string(1)).c_str());
> > > >
> > > > I’m sorry, but I cannot accept that step of the reasoning as
> > > > being
> > > > even remotely valid. Just looking at the code makes me want to
> > > > barf.
> > > >
> > > > So either I misunderstood Frediano’s point, or the reasoning is
> > > > deeply flawed in a not very subtle way.
> > >
> > > It's not just about .c_str(), it's really about any API returning
> > > a
> > > non-static string which will be freed before the exception is
> > > caught
> > > (think RAII-like cases, wrappers around preexisting C APIs, ...).
> > > I already mentioned it, but an API taking a const char *, and
> > > also
> > > mandating that this const char * must be alive as long as the
> > > instance
> > > I invoked the method from is really unusual/unexpected to me, and
> > > would
> > > thus be error-prone.
> > > Christophe
> >
> >
> > A simple concrete example:
>
> Your example is basically a variant of mine.
>
> > ------
> >
> > #include <exception>
> > #include <iostream>
> > #include <string>
> >
> > class Error final : public std::exception
> > {
> > public:
> > Error(const char *message): exception(), message(message) { }
> > virtual const char *what() const noexcept override { return
> > message; }
> > protected:
> > const char *message;
> > };
> >
> >
> > void do_it(int x)
> > {
> > std::string foo("Hello, world");
> > // do some other stuff...
> > if (x == 1)
> > throw Error(foo.c_str()); // dangerous
>
> Yes. Why did you add c.str() to start with?
To show that it's possible to use c_str() with the standard exception
class, but not with the new one.
>
> If I write this:
>
> typedef std::vector<const char *> ArgumentList;
>
> ArgumentList build_args_list(int argc, char **argv)
> {
> ArgumentList argList;
> for (int a = 1; a < argc; a++)
> {
> std::string arg = argv[a];
> // … do some other stuff
> argList.push_back(arg.c_str()); // dangerous
> }
> return argList;
> }
>
> do I have a right to complain to the writers of the standard library
> that their interface is “error prone"?
You didn't really address what I consider to be the important part of
my message: it's an exception that has the same interface as a standard
exception class, but behaves differently. That inconsistency is the
thing that makes it easy to make errors.
>
>
> > else if (x == 2)
> > throw std::runtime_error(foo.c_str()); // fine
> >
> > std::cout << "run without any arguments for Error, or with 1
> > argument for runtime_error";
> > }
> >
> > int main(int argc, char** argv)
> > {
> > try {
> > do_it(argc);
> > } catch (Error& ex) {
> > std::cout << "Error: " << ex.what() << std::endl;
> > } catch (std::runtime_error& ex) {
> > std::cout << "runtime_error: " << ex.what() << std::endl;
> > }
> >
> > return 0;
> > }
> >
> >
> > ------
> >
> > The fact that the interface is exactly the same as runtime_error
> > but it
> > behaves differently is almost the textbook definition of error-
> > prone,
> > IMO.
>
>
More information about the Spice-devel
mailing list