断点未在静态链接的lib中的全局静态初始化类中命中

时间:2010-12-29 17:59:26

标签: c++ visual-studio-2010 static constructor static-libraries

我在VS2010上经历了一个奇怪的行为。

我已将这个简单的代码添加到两个.cpp文件中,并在指定的代码行上放置了一个断点

namespace {
   class TestClass
   {
   public:
      TestClass()
      {
         printf("");     // ### BREAKPOINT_HERE
      }
   };
}
TestClass a;

奇怪的是,一旦编译并运行程序,其中一个文件中的断点是正确的,而另一个文件中的断点会自动禁用并显示警告: '断点目前不会被击中。没有为此文档加载任何符号。'

两个.cpp文件都以相同的方式创建,并具有相同的属性。 他们所在的项目有大量的文件,但我最近没有添加我遇到问题的文件 - 有更新的文件没有出现问题。

谁能告诉我可能出现什么问题?

干杯, 帕克萨斯

6 个答案:

答案 0 :(得分:1)

之前我遇到过类似的问题,尽管我已经在一个单独的LIB项目中定义了注册代码。事实证明,链接器优化了我的对象!我的解决方案是在函数中引用这些“注册对象”。然后我从我的应用程序中调用了该函数。也许这样的事情发生在这里?尝试关闭整个程序优化,看看会发生什么。您可能需要做一些事情来阻止优化器将此对象视为死代码。 (我希望Visual C ++具有将代码标记为非死的属性...)

<强>更新 如果找不到将对象标记为非死的简洁方法,我不得不触摸两个项目中的代码。在LIB项目中,我定义了函数而不是全局对象。在app项目中,我定义了调用这些函数的函数。

你可以做的是:

// Registrations.cpp
#ifdef EXPORT_REGISTRATIONS
#define REGISTRATION_CODE(theClass) \
       void register_##theClass##_function() { \
           // Code for registering your class \
       }
#else
#define REGISTRATION_CODE(theClass) \
       // Declare a function prototype for the function in LIB \
       void register_##theClass##_function(); \
       struct theClass##importer { \
           theClass##importer() { \
               // Call into LIB \
               register_##theClass##_function(); \
           } \
       } g_##theClass##importerInstance; \
#endif

REGISTRATION_CODE(MyClass);
REGISTRATION_CODE(MyOtherClass);

然后在LIB项目中,确保定义EXPORT_REGISTRATIONS。这将生成执行您打算执行的实际注册的函数。在应用项目中,确保未定义EXPORT_REGISTRATIONS 。应用项目中的#include "<path to lib project>\Registrations.cpp"。这将生成调用LIB项目中定义的函数的全局对象(如原始对象)。

这就是我解决问题的方法。你的旅费可能会改变。 :)

答案 1 :(得分:1)

您已在两个不同的文件中定义了a对象。您违反了一个定义规则,编译器可以在这种情况下随意做任何事情。

为您的一个变量指定一个不同的名称,您应该能够观察它们的初始化。

TestClass的多个定义很好,原因有两个。一个是他们在不同的命名空间中,另一个是即使他们不在不同的命名空间中,他们的定义也是如此将是相同的,并且单定义规则对此作出例外。)

答案 2 :(得分:1)

我认为问题不在于编译器,而在于链接器。如果它没有看到对库中模块中的符号的任何显式访问,则它不会链接模块。因此,该模块中的任何静态对象都不会存在。

要明白为什么会这样,请回想一下C ++之前的时间。库的目的是将您可能所需的每个功能打包到一个文件中。链接器将遍历库中的每个模块,其中通过编译单个源文件来定义模块。如果程序需要在模块中定义符号,那么该模块将被链接;如果没有,它将被跳过,以便库中未使用的部分不会使应用程序膨胀。

这个过程是递归的,所以如果模块A需要来自模块B的东西,模块B将被链接,即使程序没有直接要求它。这是解决问题的关键 - 确保库的主模块从包含静态初始化器的每个模块访问至少一个对象或函数。

答案 3 :(得分:0)

  

利用静态的事实   对象在main()之前初始化   被称为填充它

我认为您错过了允许编译器将非局部变量的动态初始化推迟到第一次使用的静态存储持续时间这一事实。如果您的代码从不使用这些注册类,则编译器将不会创建代码来执行动态初始化。

答案 4 :(得分:0)

请放弃你正在做的其他事情和read this now。 (另请阅读以下部分,10.15。)

其次,如果你偶然遇到这个问题,请问你能否展示getClassesRegistry的定义?你有没有在那里设置断点,或者使用其他诊断来验证该方法是否真的没有被调用?

答案 5 :(得分:-1)

我发现了一些可以揭示情况并编辑主要信息的新事实。

  

尝试关闭整个程序优化,看看会发生什么。您可能需要做一些事情来阻止优化器将此对象视为死代码。

整个解决方案在调试模式下编译,并关闭所有优化。文件所在的项目被编译为静态库。