【问题标题】:How to check whether main function was called?如何检查主函数是否被调用?
【发布时间】:2011-09-08 12:50:25
【问题描述】:

我想编写一些依赖于静态变量值的代码。所以我想添加一些检查以消除从其他静态变量构造函数调用此代码的可能性。并防止静态初始化命令一劳永逸。例如:

static Foo foo = Foo();

// this function should be called ONLY from main program conrol flow
// ONLY after all static variable initialization was complete! ONLY!
int bar()
{
#ifdef _DEBUG
  if(! CRT_was_initialized_and_main_function_was_called ) ShowErrorMessage();
#endif
  if(foo.somefunction() == 2) return 0; else return -1;
}

//here inattentive programmer will caught error message during debug
const int barConstant = bar();

int main()
{
  //now all is fine
  const int barConstant = bar();
}

我该怎么做?如何检查我的函数是否在 main 函数之后被调用?

更新:Foo 对象的初始化代码非常繁重,可能会很慢甚至抛出异常

UPDATE2:这样做没有生命危险。大多数情况下,bar 功能之前的注释都可以正常工作。我对某种调试检查感兴趣,以惩罚程序调试版本中粗心的程序员,而不是手动执行此操作。而且它可能是非标准方式,例如调用一些仅适用于 MSVC 的疯狂内置函数。

【问题讨论】:

  • 如果你不想在程序每次执行时都验证,你可以设置一个断点,进入Debug,看看是否初始化完成。
  • 在您的代码中,第一行是声明一个函数,而不是一个类实例。
  • 我的印象是当前的范式是“静态且全局状态不好”。以与此类似的问题为主要原因。有重构的机会吗?
  • 这个 Foo 对象实际上非常重,它的初始化需要很长时间,并且可能导致在 CRT 初始化期间不应该抛出的错误。相比之下, bar 函数应该很轻,并且在循环等代码的非常热的部分中调用。我在这里看到的唯一可能的重构是使 Foo 成为像对象一样的单例......但这将是唯一解决方案的交易。
  • 你能给main加好友吗? ideone 和 cormeau 让我可以,但我无法访问实际的编译器/链接器,因此我可以检查它在单独的翻译单元中的行为方式。您仍然有输入main 后可能会发生静态初始化的问题,但至少您会知道只有main 可以创建您的Foo 对象。

标签: c++ static main crt


【解决方案1】:

创建一个全局 bool 标志,指示是否调用了 main,最初为 false。 在main() 中将其更改为true,并且仅在此处更改。不是一个优雅的解决方案,而是一个非常奇怪的问题的解决方案。

【讨论】:

  • 最重要的是,看起来最干净。
  • 我无法访问主要功能,我来自不同的部门并在图书馆工作。这个库没有任何类型的初始化函数。
  • @ZAB:那我猜你不走运了。询问其他部门是否可以添加这样的标志。
  • @Xeo:是的!但我认为它可以在调试版本中完成,调用或检查一些带有巨大“_”前缀和奇怪名称(如_crtdll_init)的函数。此外,仅在 MSVC 上执行此检查就足够了。
【解决方案2】:

我会使用这个函数,mainCalled 是一个全局布尔变量,默认情况下为 false 并在调用 main 时更改为 true:

int bar()
{
    static bool called = 0;
    if (!mainCalled)
        ShowErrorMessage();
    if (called)
    {
        return -1;
    }
    called = true;
    //do somthing
    return 0;
}

【讨论】:

  • 如果你在主函数之外声明一个对象,它的构造函数会在main()之前运行。如果您使用涉及函数调用的表达式初始化某些全局值,则可能会发生这种情况。这可能是个问题。一个很大的,很难弄清楚。
  • 我以为我已经解决了这个问题的答案。
【解决方案3】:

您可以在函数之前简单地声明一个全局变量int main_called = 0;

在函数中检查变量是否为 0,在 main 中将变量设置为 1。

【讨论】:

    【解决方案4】:

    没有可移植的方式来做到这一点。您当然可以拥有自己的全局,在 main 中初始化,然后在 main 结束时取消初始化(不要忘记使用构造函数/析构函数来确保即使在异常退出 main 时也能正常工作)。

    在 MSVC 上,一种更简单但不可移植的方法是#prgama init_seg。这为您提供了三个预设阶段,您可以自己创建更多阶段。

    马丁

    【讨论】:

    • 你说没有便携的方法来做到这一点;然后您继续解释一种可移植的方法(main 中的全局变量)。 :-/
    • 检查 CRT 是否已初始化和启动序列是否完成没有一个糟糕的方法。你能做的最好的就是检查你是否在 main 中。当然,没有什么能阻止可移植程序从静态构造函数调用 main(即使这很奇怪也很糟糕)。
    • 所有用#pragma init_seg 标记的对象仍然会在main 之前初始化...如何将一个放在CRT 初始化列表的最后?
    • 如果你使用init_seg,你不需要在最后加一个。相反,只需将 init_seg 分组适当地应用于每个构造函数,以确保按层进行适当的排序。但是,您可以使用自己的部分名称进入最后。
    【解决方案5】:

    这对于单例模式来说不是一个很好的例子吗?可以检查它自己的初始化吗?

    【讨论】:

    • 有点,但这个愚蠢的 Foo 对象可能会抛出异常或减慢程序初始化。您不能阻止静态对象在其自己的初始化阶段实例化单例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 2017-04-02
    • 1970-01-01
    相关资源
    最近更新 更多