Date: Mon, 22 Jun 2020 20:44:08 +0100
Thanks again for pointing to a dark corner in my proposal.
For the template problem I can see at least 2 solutions (and would
propose allowing both in the language).
1. Traditional-style: if the templates be the problem, let the templates
be the solution:
============================================================
class storage_exception { ... };
class database_exception { ... };
template <class T> struct storage_traits
{
const int max_record_length = 1000;
using service_exception = storage_exception;
};
template <> struct storage_traits<database_storage>
{
const int max_record_length = 2000;
using service_exception = database_exception;
};
class file_storage
{
bool read_record(char record[1000]) throw(storage_exception);
};
class database_storage
{
bool read_record(char record[2000]) throw(database_exception);
};
template <class T>
void validate(T & storage) throw(typename
storage_traits<T>::service_exception)
{
char record[storage_traits<T>::max_record_length];
while (storage.read_record(record)) ...
}
============================================================
2. New-style: let the compiler deduce (the "auto" keyword already means
"let compiler determine the type"!)
============================================================
class storage_exception { ... };
class database_exception { ... };
template <class T> struct storage_traits
{
const int max_record_length = 1000;
}
template <> struct storage_traits<database_storage>
{
const int max_record_length = 2000;
}
class file_storage
{
bool read_record(char record[1000]) throw(storage_exception);
}
class database_storage
{
bool read_record(char record[2000]) throw(database_exception);
}
template <class T>
void validate(T & storage) throw(auto) // the compiler "knows" which
"checked" exceptions are not handled
{
char record[storage_traits<T>::max_record_length];
while (storage.read_record(record)) ... // the compiler "knows" what
is "checked" for "T::readRecord()"
}
============================================================
What do you think?
I agree when you say "C++ exceptions are what you use when you want the
set of yieldable things to be open-ended", and I'm not proposing to
change that. I just want the programmer to be able to choose between
open-ended-ness and safety on a per-function basis.
Andrey Kapustin
On 21/06/2020 23:15, Arthur O'Dwyer via Std-Proposals wrote:
> On Sun, Jun 21, 2020 at 5:10 PM Michael Hava via Std-Proposals
> <std-proposals_at_[hidden]
> <mailto:std-proposals_at_[hidden]>> wrote:
>
> To quote the recent HOPL paper
> <https://www.stroustrup.com/hopl20main-p5-p-bfc9cd4--final.pdf> by
> Bjarne:
>
> Page 62: "There was always a group of people who wanted
> compile-time checks of which exceptions a
> function could throw. That, of course, works well in type theory,
> with small programs, with fast
> compilers, and with full control of the source code. The committee
> repeatedly rejected that idea on
> the grounds that it doesn’t scale to million-line programs
> developed and maintained by dozens (or
> more) organizations."
>
>
> Concretely, one of the ways C++ lets you write big programs "without
> full control of the source code" is templates.
> Andrey, you'd have to figure out what to do about templates:
>
> struct ErrA {};
> struct ErrB {};
> struct SA {
> void foo() throw(ErrA);
> };
> struct SB {
> void foo() throw(ErrB);
> };
> template<class T>
> void foo(T t) throw(???) {
> t.foo();
> }
>
> C++ already has several properties that can be propagated from callee
> to caller with enough effort: noexceptness, well-formedness (SFINAE).
> It has a few that cannot be propagated without loss: explicitness,
> overload-resolution-priority, constrainedness (C++20).
> At the moment, you're asking to add another property that can't be
> propagated. Can you fix that somehow?
>
> Herb Sutter's "Herbceptions" have basically the same problem with
> templates (last I checked): they add a new property that we'd like to
> propagate but it is ugly and/or inefficient to do so. I refer to these
> kinds of property as "colorations"; see e.g.
> https://quuxplusone.github.io/blog/2018/03/16/async-roundup/#the-concept-of-function-coloring (less
> about herbceptions)
> https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/A05A5feSZYI (more
> about herbceptions)
>
> In general I kind of like the idea of statically checking "this
> function may yield a result /or/ an ErrA, and so all callers must
> handle ErrA"; but if you're actually in that situation today, why not
> just return a `variant` or `Expected` type instead? C++ exceptions are
> what you use when you /want/ the set of yieldable things to be open-ended.
>
> –Arthur
>
For the template problem I can see at least 2 solutions (and would
propose allowing both in the language).
1. Traditional-style: if the templates be the problem, let the templates
be the solution:
============================================================
class storage_exception { ... };
class database_exception { ... };
template <class T> struct storage_traits
{
const int max_record_length = 1000;
using service_exception = storage_exception;
};
template <> struct storage_traits<database_storage>
{
const int max_record_length = 2000;
using service_exception = database_exception;
};
class file_storage
{
bool read_record(char record[1000]) throw(storage_exception);
};
class database_storage
{
bool read_record(char record[2000]) throw(database_exception);
};
template <class T>
void validate(T & storage) throw(typename
storage_traits<T>::service_exception)
{
char record[storage_traits<T>::max_record_length];
while (storage.read_record(record)) ...
}
============================================================
2. New-style: let the compiler deduce (the "auto" keyword already means
"let compiler determine the type"!)
============================================================
class storage_exception { ... };
class database_exception { ... };
template <class T> struct storage_traits
{
const int max_record_length = 1000;
}
template <> struct storage_traits<database_storage>
{
const int max_record_length = 2000;
}
class file_storage
{
bool read_record(char record[1000]) throw(storage_exception);
}
class database_storage
{
bool read_record(char record[2000]) throw(database_exception);
}
template <class T>
void validate(T & storage) throw(auto) // the compiler "knows" which
"checked" exceptions are not handled
{
char record[storage_traits<T>::max_record_length];
while (storage.read_record(record)) ... // the compiler "knows" what
is "checked" for "T::readRecord()"
}
============================================================
What do you think?
I agree when you say "C++ exceptions are what you use when you want the
set of yieldable things to be open-ended", and I'm not proposing to
change that. I just want the programmer to be able to choose between
open-ended-ness and safety on a per-function basis.
Andrey Kapustin
On 21/06/2020 23:15, Arthur O'Dwyer via Std-Proposals wrote:
> On Sun, Jun 21, 2020 at 5:10 PM Michael Hava via Std-Proposals
> <std-proposals_at_[hidden]
> <mailto:std-proposals_at_[hidden]>> wrote:
>
> To quote the recent HOPL paper
> <https://www.stroustrup.com/hopl20main-p5-p-bfc9cd4--final.pdf> by
> Bjarne:
>
> Page 62: "There was always a group of people who wanted
> compile-time checks of which exceptions a
> function could throw. That, of course, works well in type theory,
> with small programs, with fast
> compilers, and with full control of the source code. The committee
> repeatedly rejected that idea on
> the grounds that it doesn’t scale to million-line programs
> developed and maintained by dozens (or
> more) organizations."
>
>
> Concretely, one of the ways C++ lets you write big programs "without
> full control of the source code" is templates.
> Andrey, you'd have to figure out what to do about templates:
>
> struct ErrA {};
> struct ErrB {};
> struct SA {
> void foo() throw(ErrA);
> };
> struct SB {
> void foo() throw(ErrB);
> };
> template<class T>
> void foo(T t) throw(???) {
> t.foo();
> }
>
> C++ already has several properties that can be propagated from callee
> to caller with enough effort: noexceptness, well-formedness (SFINAE).
> It has a few that cannot be propagated without loss: explicitness,
> overload-resolution-priority, constrainedness (C++20).
> At the moment, you're asking to add another property that can't be
> propagated. Can you fix that somehow?
>
> Herb Sutter's "Herbceptions" have basically the same problem with
> templates (last I checked): they add a new property that we'd like to
> propagate but it is ugly and/or inefficient to do so. I refer to these
> kinds of property as "colorations"; see e.g.
> https://quuxplusone.github.io/blog/2018/03/16/async-roundup/#the-concept-of-function-coloring (less
> about herbceptions)
> https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/A05A5feSZYI (more
> about herbceptions)
>
> In general I kind of like the idea of statically checking "this
> function may yield a result /or/ an ErrA, and so all callers must
> handle ErrA"; but if you're actually in that situation today, why not
> just return a `variant` or `Expected` type instead? C++ exceptions are
> what you use when you /want/ the set of yieldable things to be open-ended.
>
> –Arthur
>
Received on 2020-06-22 14:47:23