Boost logo

Boost :

Subject: Re: [boost] [Fit] Review
From: Lee Clagett (forum_at_[hidden])
Date: 2016-03-12 21:35:27


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


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