jeudi 28 février 2019

How can `std::error_category` safely be used in static destructors?

Consider a custom error type written using the LLVM system_category implementation for reference:

#include <iostream>
#include <system_error>

struct my_error_category_type : std::error_category {
    char const* name() const  noexcept override { return "name"; }
    std::string message(int i) const noexcept override{ return "message"; }
    ~my_error_category_type() {
        std::cout << "Destroyed the category" << std::endl;
    }
};

std::error_category const& my_error_category() noexcept {
    static my_error_category_type c;
    return c;
}

Now imagine the following simple class that uses std::error_code to handle errors:

std::error_code do_some_setup() {
    return std::error_code(1, my_error_category());
}
std::error_code do_some_cleanup() {
    return std::error_code(2, my_error_category());
}

struct MyObj {
    void method() {
        // this constructs the category for the first time
        auto err = do_some_setup();
        std::cout << err << std::endl;
    }

    ~MyObj() {
        std::cout << "Running cleanup" << std::endl;
        auto err = do_some_cleanup();
        std::cout << err << std::endl;
    }
};

The following code gives alarming output

static MyObj obj;

int main() {
    obj.method();  // remove this line, and the output is fine
}

name:1
Destroyed the category
Running cleanup
name:2

Note how my_error_category_type::message was called on a destructed object!

My questions are:

  1. Is calling message on this destructed object safe?
  2. If not, is there a way to preserve the lifetime of the category? Can I make the object immortal somehow?
  3. Does the standard make any guarantees about the lifetime of the builtin std::system_category() objects and the like? The LLVM implementation I link to above suffers exactly the same problem.

Aucun commentaire:

Enregistrer un commentaire