在宏内的 if 语句中否定表达式会产生奇怪的结果
Negating expression in if statement inside macro gives odd results
我 运行 遇到了一个有点奇怪的问题。这让我觉得答案非常明显,我只是没有看到任何东西,因为代码太简单了。
我基本上有一个名为“ASSERT”的宏来确保值不为假。如果是,它会向控制台写入一条消息并中断调试。
我的问题是,当断言从 std::string::find_first_of(...) 返回的索引不等于 std::string::npos 时,断言似乎根本不起作用。每次,断言都会失败。
我已验证发生调试中断时值不相等,所以我看不出断言是如何失败的。
在我的原始代码中,它将数据从文件读取到字符串,但问题似乎仍然存在于下面的示例中,除了一个 const std::string.
我正在做一个更大的项目,但这里有一个重现错误的最小示例(顺便说一句,我使用的是 Visual Studio 2022 和 C++17):
#include <iostream>
#include <string>
#define ASSERT(x, msg) {if(!x) { std::cout << "Assertion Failed: " << msg << "\n"; __debugbreak(); } }
int main() {
const std::string source = "Some string that\r\n contains the newline character: \r\n...";
size_t eol = source.find_first_of("\r\n", 0);
ASSERT(eol != std::string::npos, "Newline not present!");
// Other code...
return 0;
}
请注意,即使字符串中只有一个换行符 ("\r\n"),也会发生完全相同的事情。
有趣的是,“eol”似乎在我 运行 的每个测试用例中都有正确的值。唯一错误的是断言,所以如果我忽略它并继续,一切 运行 完全符合我的预期。
我也发现了这个似乎相关的问题,但没有得到答案或结论:
std::string::find_first_of does not return the expected value
已在评论中提供答案。解决方案是简单地在宏的“!x”部分中的 x 周围添加括号。这转化为以下内容(归功于 0x5453):
#define ASSERT(x, msg) {if(!(x)) { ... } }
之所以解决这个问题,是因为如果您展开原始宏,它会看起来像这样(这是它的编译方式):
if(!eol != std::string::npos) {...}
这显然是不正确的,因为您想检查整个条件是否为假。像这样:
if(!(eol != std::string::npos)) {...}
添加括号可以解决这个问题。
简单,但仍然是一个很好的提醒,要检查像这样的小事情。
这是预处理器的宏替换引擎的设计简单愚蠢的意外结果。提供给宏的表达式不会像函数那样被求值,文本会在替换过程中直接插入。
给出
#define ASSERT(x, msg) {if(!x) { std::cout << "Assertion Failed: " << msg << "\n"; __debugbreak(); } }
行
ASSERT(eol != std::string::npos, "Newline not present!");
将转化为
{if(!eol != std::string::npos) { std::cout << "Assertion Failed: " << "Newline not present!" << "\n"; __debugbreak(); } }
并且 !
仅应用于 eol
,将宏的预期行为更改为无意义的东西。
添加评论中推荐的额外括号
#define ASSERT(x, msg) {if(!(x)) { std::cout << "Assertion Failed: " << msg << "\n"; __debugbreak(); } }
结果
{if(!(eol != std::string::npos)) { std::cout << "Assertion Failed: " << "Newline not present!" << "\n"; __debugbreak(); } }
现在在应用 !
和测试之前正在评估表达式。
因为 macros are "evil",并且由于宏不使用任何特殊的、位置相关的调试宏,如 __FILE__
或 __LINE__
,在这种情况下我将替换宏使用函数并依靠编译器的优化来内联它。
我 运行 遇到了一个有点奇怪的问题。这让我觉得答案非常明显,我只是没有看到任何东西,因为代码太简单了。
我基本上有一个名为“ASSERT”的宏来确保值不为假。如果是,它会向控制台写入一条消息并中断调试。 我的问题是,当断言从 std::string::find_first_of(...) 返回的索引不等于 std::string::npos 时,断言似乎根本不起作用。每次,断言都会失败。
我已验证发生调试中断时值不相等,所以我看不出断言是如何失败的。
在我的原始代码中,它将数据从文件读取到字符串,但问题似乎仍然存在于下面的示例中,除了一个 const std::string.
我正在做一个更大的项目,但这里有一个重现错误的最小示例(顺便说一句,我使用的是 Visual Studio 2022 和 C++17):
#include <iostream>
#include <string>
#define ASSERT(x, msg) {if(!x) { std::cout << "Assertion Failed: " << msg << "\n"; __debugbreak(); } }
int main() {
const std::string source = "Some string that\r\n contains the newline character: \r\n...";
size_t eol = source.find_first_of("\r\n", 0);
ASSERT(eol != std::string::npos, "Newline not present!");
// Other code...
return 0;
}
请注意,即使字符串中只有一个换行符 ("\r\n"),也会发生完全相同的事情。
有趣的是,“eol”似乎在我 运行 的每个测试用例中都有正确的值。唯一错误的是断言,所以如果我忽略它并继续,一切 运行 完全符合我的预期。
我也发现了这个似乎相关的问题,但没有得到答案或结论: std::string::find_first_of does not return the expected value
已在评论中提供答案。解决方案是简单地在宏的“!x”部分中的 x 周围添加括号。这转化为以下内容(归功于 0x5453):
#define ASSERT(x, msg) {if(!(x)) { ... } }
之所以解决这个问题,是因为如果您展开原始宏,它会看起来像这样(这是它的编译方式):
if(!eol != std::string::npos) {...}
这显然是不正确的,因为您想检查整个条件是否为假。像这样:
if(!(eol != std::string::npos)) {...}
添加括号可以解决这个问题。
简单,但仍然是一个很好的提醒,要检查像这样的小事情。
这是预处理器的宏替换引擎的设计简单愚蠢的意外结果。提供给宏的表达式不会像函数那样被求值,文本会在替换过程中直接插入。
给出
#define ASSERT(x, msg) {if(!x) { std::cout << "Assertion Failed: " << msg << "\n"; __debugbreak(); } }
行
ASSERT(eol != std::string::npos, "Newline not present!");
将转化为
{if(!eol != std::string::npos) { std::cout << "Assertion Failed: " << "Newline not present!" << "\n"; __debugbreak(); } }
并且 !
仅应用于 eol
,将宏的预期行为更改为无意义的东西。
添加评论中推荐的额外括号
#define ASSERT(x, msg) {if(!(x)) { std::cout << "Assertion Failed: " << msg << "\n"; __debugbreak(); } }
结果
{if(!(eol != std::string::npos)) { std::cout << "Assertion Failed: " << "Newline not present!" << "\n"; __debugbreak(); } }
现在在应用 !
和测试之前正在评估表达式。
因为 macros are "evil",并且由于宏不使用任何特殊的、位置相关的调试宏,如 __FILE__
或 __LINE__
,在这种情况下我将替换宏使用函数并依靠编译器的优化来内联它。