C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Fwd: Catch exception thrown from constructor of global object

From: Edward Catmur <ecatmur_at_[hidden]>
Date: Thu, 12 Jan 2023 17:08:24 +0000
On Thu, 12 Jan 2023 at 16:52, Jason McKesson via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> ---------- Forwarded message ---------
> From: Jason McKesson <jmckesson_at_[hidden]>
> Date: Thu, Jan 12, 2023 at 11:51 AM
> Subject: Re: [std-proposals] Catch exception thrown from constructor
> of global object
> To: Edward Catmur <ecatmur_at_[hidden]>
>
>
> On Thu, Jan 12, 2023 at 11:40 AM Edward Catmur <ecatmur_at_[hidden]>
> wrote:
> >
> > On Thu, 12 Jan 2023 at 16:17, Jason McKesson via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
> >>
> >> On Thu, Jan 12, 2023 at 10:34 AM Federico Kircheis via Std-Proposals
> >> <std-proposals_at_[hidden]> wrote:
> >> > On 2023-01-12 at 11:23, Frederick Virchanza Gotham via Std-Proposals
> >> > wrote:
> >> >
> >> > >
> >> > >Now if you were to ask me . . . . I would almost go so far as to
> >> > >propose that C++26 should deprecate defining a global object that
> >> > >doesn't satisfy 'is_trivial' so that all such objects would be
> wrapped
> >> > >in an std::optional<T> and emplaced within the body of 'main', but of
> >> > >course I know that I won't get any support on that.
> >> >
> >> >
> >> > It would also not cover most use-cases, for example
> >> >
> >> >
> >> > ----
> >> > /* const */ int i = function_that_throws();
> >> >
> >> > int main(){
> >> > }
> >> > ----
> >> >
> >> >
> >> > Also std::optional as global variable is problematic if inside a
> >> > library, depending on the visibility of the symbols, the destructor
> >> > might be executed more than once (and thus be UB)
> >>
> >> It seems to me that, to the extent that this is a significant problem,
> >> static analysis is likely the most effective solution. Static analysis
> >> tools should be able to detect if the initializer for a global is
> >> `noexcept` and there can be options to have such tools give a warning
> >> if it isn't. Better tools might be able to follow inlined functions to
> >> see if anything could actually throw given the value of parameters.
> >
> >
> > As Lénárd says above, this already exists: `constinit`.
>
> `constinit` means that the initializer must be a constant expression.
> But it's not hard to write an initializer that isn't a constant
> expression yet does not throw exceptions. Constant expressions cannot
> throw exceptions (yet), but there are many expressions that also don't
> throw exceptions.
>

Sure, but maybe those should be constant expressions. Other than
allocation, what is there to do in a constructor that can't already be
evaluated in constant expressions?

> struct X {
> > constexpr X(int i) {
> > if (i == 99)
> > throw i;
> > }
> > };
> > constinit X x1 = 1; // OK
> > constinit X x99 = 99; // ill-formed, X::X(99) throws
> >
> > It's perhaps not quite as useful yet as it might be, but once we get
> non-transient constexpr allocation (TM) there will be little that you can't
> do in a constant-initializer.
>
> Has there been any movement on non-transient allocation? Because I got
> the impression that this was deemed too difficult.
>

P2670R0 was in the last-but-one mailing. I don't think it's been reviewed
yet; cf. https://github.com/cplusplus/papers/issues/1336

Also, that doesn't change the fact that you can't do things like open
> files and the like, things which don't throw exceptions but also
> cannot be constant expressions.
>

Those can fail, so they probably should throw exceptions when done from a
constructor. Otherwise, you're just caching failure to report later, so you
may as well just cache the constructor arguments and lazy-open the file on
first use.

Received on 2023-01-12 17:08:38