On Thu, 12 Jan 2023 at 18:16, Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
---------- Forwarded message ---------
From: Jason McKesson <jmckesson@gmail.com>
Date: Thu, Jan 12, 2023 at 1:16 PM
Subject: Re: [std-proposals] Fwd: Catch exception thrown from
constructor of global object
To: Edward Catmur <ecatmur@googlemail.com>


On Thu, Jan 12, 2023 at 12:08 PM Edward Catmur <ecatmur@googlemail.com> wrote:
>
> On Thu, 12 Jan 2023 at 16:52, Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
>>
>> ---------- Forwarded message ---------
>> From: Jason McKesson <jmckesson@gmail.com>
>> 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@googlemail.com>
>>
>>
>> On Thu, Jan 12, 2023 at 11:40 AM Edward Catmur <ecatmur@googlemail.com> wrote:
>> >
>> > On Thu, 12 Jan 2023 at 16:17, Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
>> >>
>> >> On Thu, Jan 12, 2023 at 10:34 AM Federico Kircheis via Std-Proposals
>> >> <std-proposals@lists.isocpp.org> 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?

As I mentioned later, you cannot open files (and no, failure to open a
file is not necessarily a failure to construct an object whose
constructor opens the file as part of its work. There may be some
fallback default behavior). Even if such APIs were able to be constant
expressions, the file may not exist at compile time. Or you may be
compiling for a completely different system.

That seems a lot to be doing before entering `main`, but I accept the point.

Basically, any OS system calls cannot be constant expressions.

Yes, and there are some that can't fail, or calls like `std::chrono::system_clock::now()` (which is `noexcept`). Also apparently calling `getenv` is valid before entering `main`, and you may well want to use that to inform static initialization: https://stackoverflow.com/questions/437279/is-it-safe-to-use-getenv-in-static-initializers-that-is-before-main