Boost logo

Boost :

Subject: Re: [boost] [Fit] Review
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2016-03-13 06:43:15


Le 13/03/2016 03:35, Lee Clagett a écrit :
> On Fri, 11 Mar 2016 19:41:22 +0000 (UTC)
> paul Fultz <pfultz2_at_[hidden]> wrote:
>
>>> On Friday, March 11, 2016 12:30 PM, Bjorn Reese
>>> <breese_at_[hidden]> wrote:
>>>> On 03/11/2016 01:50 AM, Paul Fultz II wrote:
>>>
>>>> library. I do, however, need to discuss some of the misperceived
>>>> issues
>>> with
>>>> using global function objects in the documentation. As the issues
>>>> raised in the review were:
>>> The destruction order of global objects in different translation
>>> units is undefined. This means that I cannot use a global fit
>>> function in the destructor of one of my own global objects because
>>> the former may have
>>> been destroyed before the latter.
>> This is not a problem, because it is initialized at compile-time,
>> that means it is a literal type:
>>
>> http://en.cppreference.com/w/cpp/concept/LiteralType
>>
>>
>> So besides the constructor not having side-effects, the destructor
>> must be trivial, as well. For example, this cannot compile:
>>
>>
>> struct foo_fn
>> {
>> template<class... Ts>
>> auto operator()(Ts&&...) const
>> {}
>>
>> ~foo_fn()
>> {
>> std::cout << "Hello";
>> }
>> };
>>
>> BOOST_FIT_STATIC_FUNCTION(foo) = foo_fn();
>>
>> int main() {
>> foo();
>> }
>>
>> So with trivial destructors, the order of destruction doesn't matter.
>> Furthermore, on most compilers(that is everything but MSVC), the
>> function is the same across all translation units. So there is only
>> one function in the executable, not one per translation unit. Taking
>> the address of the function will yield the same address across all
>> translation units. This is the same way functions work.
>>
> The following program throws an exception and aborts in Clang 3.4 and
> Gcc 4.8, both on Ubuntu 14.04
>
> #include <iostream>
> #include <stdexcept>
> #include <fit/function.hpp>
> #include <fit/static.hpp>
>
> class log_ {
> public:
> log_()
> : active_(true) {
> std::cout << "constructing log" << std::endl;
> // or some equally terrible global state changing
> // code such as a file open
> }
>
> ~log_() {
> std::cout << "destructing log" << std::endl;
> active_ = false;
> }
>
> void operator()(const char* const msg) const {
> if (active_) {
> std::cout << msg << std::endl;
> } else {
> throw std::runtime_error{"not active"};
> }
> }
> private:
> bool active_;
> };
>
> FIT_STATIC_FUNCTION(log) = fit::static_<log_>{};
>
> struct one_ {
> ~one_() {
> log("destructing one");
> }
> };
>
> struct two_ {
> two_() {
> log("constructing two");
> }
> };
>
> const one_ one{};
> const two_ two{};
>
> int main() {
> return 0;
> }
>
> Both Gcc and Clang print:
>
> constructing log
> constructing two
> destructing log
> terminate called after throwing an instance of 'std::runtime_error'
> what(): not active
>
> The generated program conforms to the C++ standard because `log_` was
> created as a function local static, and the call to `operator()` after
> destruction can do _anything_ (undefined behavior). Also, `log_` can be
> constructed before `one_` and `two_` (and therefore destructed after
> both) as well, and still conform to the C++ specifications. Or at least
> that is my understanding of static construction/destruction, which is
> complex in C++.
>
> Lee

Tanks Lee for this example.

I believe that static_ needs to require that the the parameter is
is_trivially_destructible [1].

In [2] we can read " The |static_| adaptor is a static function adaptor
that allows any default-constructible function object to be
static-initialized.".

It seems the implementation doesn't do the check. Please, could you add
an issue on Github?

Vicente

[1]
http://www.cplusplus.com/reference/type_traits/is_trivially_destructible/
[2] http://pfultz2.github.io/Fit/doc/html/static/index.html


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk