预处理器定义在发布模式下工作,而不是调试

Posted

技术标签:

【中文标题】预处理器定义在发布模式下工作,而不是调试【英文标题】:Preprocessor definitions work in Release mode, not Debug 【发布时间】:2019-04-18 13:36:45 【问题描述】:

我正在尝试在一个源文件 hw_model.cpp 中定义一个预处理器宏,并在另一个 main.cpp 中保留它的未定义。

当我在 x64 发布模式 (Visual Studio 2017) 下运行这个程序时,一切都很好。当我在 x64 调试模式下运行时,main.cpp 中的#undef ACT_LIKE_HARDWARE 语句似乎被忽略了。

模型.h:

extern Signal signal;
void setSignalsFromHw();


// code below is inside some function :
#ifdef ACT_LIKE_HARDWARE
    // perform a procedure that behaves like hardware
    // This C code is supposed to act like hardware
    // hardware has the freedom to write to read-only variables
#else
    // perform some other procedure that firmware can normally do
#endif

main.cpp:

#undef ACT_LIKE_HARDWARE

#include "model.h" 

Signals signal;

int main()
    uint32_t read_data = signal.wv0;
    // do some processing on the read_data

    setSignalsFromHw();

hw_model.cpp:

#define ACT_LIKE_HARDWARE

#include "macro.h"

// Perform a cast on the "signal" global variable
// This cast operation is done so that "signal_HWModel" has the freedom to write to read-only fields
Signals_HWModel* signal_HWModel = reinterpret_cast<Signals*>(&signal);

void setSignalsFromHw()
    ntfySignal_HWModel->wv0 = 0x2;
    ntfySignal_HWModel->wv1 = 0x4;


首先,我应该在某些源文件中定义预处理器宏,而在其他源文件中取消定义它吗?如果没有,什么是好的解决方法?请考虑 model.h 比我在这里提出的要复杂得多并且重构起来有些困难的事实,但我绝对愿意接受所有建议。

其次,为什么在调试模式和发布模式下运行有区别?

谢谢

------------- ** 更新 ** --------------

根据要求,这是最少的可运行代码。它说明了我上面遇到的同样问题。对不起,如果它看起来与上面的略有不同。我只是小心不要透露太多 IP。

重要提示:我正在使用 Google 测试框架以及它们提供的 main() 函数。在 Release 中,我得到了预期的行为(undef 语句有效,并且 define 语句有效)。在 Debug 中,预处理器语句之一不会被命中。

test.cpp

#undef ACT_LIKE_HW

#include "gtest/gtest.h"
#include "model.h"

TEST(TestCaseName, TestName) 
    setSignalsFromHW();
    Block::assignSig();

hw_model.cpp

#define ACT_LIKE_HW
#include "model.h"

void setSignalsFromHW() 
    Block::assignSig();

模型.h

#pragma once

#include <iostream>
using namespace std;

void setSignalsFromHW();

class Block 
    public:
        static void assignSig() 
        #ifdef ACT_LIKE_HW
            cout << "Performing hardware operations" << endl;
        #else
            cout << "Procedures that firmware usually executes" << endl;
        #endif
        
;

但是,如果我创建一个全新的 C++ 项目(不使用 Google 测试,因此它是一个常规的控制台应用程序),其中一个预处理器语句不会同时针对 Release 和 Debug,因此无法达到目的我定义了这两个预处理器语句。我可能最终会重构代码,但我仍然想知道为什么我的 Google 测试项目会出现这个问题。

【问题讨论】:

您是否收到任何警告?因为如果事情发生变化,您可能会有未定义的行为。 你确定不只在发布模式的设置中定义宏吗? 您确定在两种配置中都定义了ACT_LIKE_HARDWARE 吗?在 Visual Studio 属性面板中很容易只设置“Active(Release)”或“Active(Win32)”而不是“All Configurations”和/或“All Platforms”。 请尝试创建minimal reproducible example 向我们展示。您的问题背后可能有数百个原因,但除了根据我们目前掌握的信息猜测之外,真的不可能做任何事情。 A minimal reproducible example 可能会显示您有一个 ODR 违规,其中两个不同的函数具有相同的名称,具体取决于定义,链接器只保留一个副本 【参考方案1】:

您违反了单一定义规则。

如果您在一个程序中对同一个符号有多个定义,那么如果两个定义不相同,您的程序就有未定义的行为。

链接器通常会删除重复的符号,因此只有一个函数会继续存在,两个调用站点最终都会调用相同的函数。

如果函数被编译器内联,那么函数中的代码将集成到调用函数中,链接器不会干扰,因此您的代码可以正常工作。这很脆弱,因为当调试中没有发生内联时,或者当您的函数增长超过编译器的内联大小阈值时,您将再次回到只有一个函数。

要修复它,请将布尔参数传递给控制您想要的行为的函数。如果这个函数是内联的,编译器应该删除未使用的代码分支。

或者将布尔参数设为模板参数并使用if constexpr,这将保证删除未使用的代码。

【讨论】:

以上是关于预处理器定义在发布模式下工作,而不是调试的主要内容,如果未能解决你的问题,请参考以下文章

发布版本适用于 Visual Studio 而不是资源管理器

装饰器模式的继承

我的代码在调试模式下工作,但不在发布模式下

[xamarin][uwp][custom renderer] 自定义渲染器 pcl 库未在发布模式下加载

设计模式整理_迭代器模式

GCC 转储预处理器定义