这"Idiom"定义明确吗?

Is This "Idiom" Well-Defined?

本文关键字:定义 Idiom      更新时间:2023-10-16

假设我有一个C库,它有自己的(反)初始化例程,就像许多库一样。

init_API();
deinit_API();

现在假设我想为用户提供另一层抽象,并使用静态实例化的类抽象掉这些调用。我正在考虑的方法:

struct API_initializer{
    API_initializer(){
        init_API();
        if(API_init_failure)
            throw (APIFailureException); //important
    }
    ~API_initializer(){
        deinit_API();
    }
};
struct API_initializer_holder{
    static API_initializer initializer;
};

现在,我的问题是,这是定义良好的行为吗?也就是说,静态构造函数是否会在某个合理的点被调用,以及C API是否需要正确初始化所有(静态)变量?此外,抛出用户无法捕获的异常是不好的做法吗?

根据我的经验,尝试依赖静态初始化顺序是一个坏主意。

一个更好的办法是去掉holder,直接输入:

int main()
{
    API_initializer foo;
    // rest of program
}

如果你真的想让它在失败时抛出,那么就包含一个try..catch块。

NB。将类设置为不可复制的,以防止发生意外。

不,如果初始化失败,程序将中断。您无法捕获异常并处理它,因为全局变量(静态成员数据)在main()之前初始化。

最好在main()中称init_API()deinit_API()

这是§3.6.2 [basic.start]所涵盖的。/p4-6的标准:

是由实现定义的具有静态存储持续时间的非局部变量在main的第一个语句。如果初始化延迟到第一次陈述后的时间点,应当发生在主语句之前第一次禁止使用在同一目录中定义的函数或变量翻译单元作为要初始化的变量35

[…]

如果具有静态或线程存储时间的非局部变量的初始化通过异常退出,则调用std::terminate

35具有静态存储时间的非局部变量,如果初始化有副作用,即使它不被odr使用,也必须初始化。

因为你的静态对象不可能与你包装的C库中的函数在同一个翻译单元中,所以保证初始化的唯一方法是将它定义在与main()相同的翻译单元中,这并没有给最终用户增加任何便利。

在任何情况下,如果初始化失败,通过抛出异常来调用std::terminate可能不是一个好主意。最终用户可能希望做更好的错误处理。

答案是:视情况而定。如果实现了外部API(这样它就不会有动态初始化),而且它是不要在任何静态初始化器中使用(应该是在这种情况下,既然他们没有办法安全地做init_API),那么这个习语或多或少是安全的(除了你不应该这样做)允许异常从静态初始化器转义。是否这是一个好主意还是不好是另一个问题;如果初始化可能失败,您可能不想之前执行它你可以捕获并处理这个错误,这意味着你成功了main