【问题标题】:c++ react to external #definec ++对外部#define做出反应
【发布时间】:2019-07-23 20:12:27
【问题描述】:

glfw3-library 可以使用包含之前的定义来包含 vulkan:

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

我想做类似的事情,但对于调试与非调试模式。 我尝试了以下方法:

main.cpp:

#define DEBUG_ENABLED
#include "someFile.hpp"

someFile.hpp:

#ifdef DEBUG_ENABLED
  std::vector<const char*> _debugExtensions = {
    VK_EXT_DEBUG_UTILS_EXTENSION_NAME
  };
#endif
#ifndef DEBUG_ENABLED
  std::vector<const char*> _debugExtensions = {};
#endif

我通过打印向量的大小检查了跨头文件的“传递定义”是否有效,但收到零,这表明编译器没有'不考虑在 someFile.hpp 中定义 DEBUG_ENABLED。

然后我查看了 stackoverflow 以了解我如何对外部文件的定义做出反应,并找到了这篇文章:

Is it possible to use #define from other cpp file?

那里接受的答案声称

其他翻译单元看不到源文件中的定义。实现文件单独编译。

我对这个答案的问题是我的经验告诉我相反,因为上面提到的包含的 glfw 清楚地对我所做的 GLFW_INCLUDE_VULKAN 定义做出了反应
一个人如何能够实现这种行为(能够对外部源文件中的定义做出反应)?

【问题讨论】:

  • 1. VK_EXT_DEBUG_UTILS_EXTENSION_NAME 是如何定义的? 2. 你能提供一个minimal reproducible example 来演示大小查询是哪个文件(以及如何)吗?
  • 你可能想在编译器命令行上定义你的常量——比如-Dfoo..
  • 我怀疑并非所有包含someFile.hpp 的cpp 都直接或间接定义了相同的东西。
  • 我认为您将翻译单元与源文件混淆了,它们不是一回事。翻译单元是在所有#includes 已被包含文件替换(并且其他预处理已完成)之后的源文件。顺便说一句,您的示例应该有效,所以我认为您的代码还有其他问题。
  • 另外,尝试标记您的全局constexpr 并使用std::array 而不是矢量。这将使用常量初始化,因此不受初始化顺序问题的影响。

标签: c++ include header-files preprocessor-directive


【解决方案1】:

我被要求创建一个“最小的可重现示例”。
这个例子表明问题确实与我假设的不同。
这个例子成功地没有重现错误,但它有助于回答我最初的问题的一部分。

不要认为这是一个好的解决方案!提供此示例是为了澄清 cpp 的行为,而不是作为在项目中实施的解决方案!

main.cpp:

#define DEBUG_ENABLED
#include "someFile.hpp"

int main() {
    Test test = Test();
    test.doCount();

    return 0;
}

someFile.hpp

#pragma once

#include <vector>

class Test {
public:
    #ifdef DEBUG_ENABLED
        std::vector<const char*> _debugExtensions = {
            "one",
            "two"
        };
    #endif
    #ifndef DEBUG_ENABLED
        std::vector<const char*> _debugExtensions = {};
    #endif

    void doCount();
};

someFile.cpp:

#include <stdio.h>

#include "someFile.hpp"

void Test::doCount() {
    printf("count: %lu", _debugExtensions.size());    
}

编译并运行它:

g++ -g main.cpp someFile.cpp -o out
./out

表明定义确实使包含的头文件中的预处理器指令做出相应的反应。 错误行为(如 cmets 所指出的)是由于包含不同的标头导致的,其中只有一个子集定义了 DEBUG_ENABLED。

此处的示例仍然容易出现该问题!

(感谢评论的人)
解决方案有多种可能性:

  1. 使用编译器定义有问题的常量,如下所示:
g++ -DDEBUG_ENABLED
  1. 确保行为依赖于此类常量定义的文件仅包含在单个翻译单元中,或者所有翻译单元在常量定义方面保持一致。这几乎排除了保留单独的 .h(.hpp) 和 .cpp 文件的可能性。

【讨论】:

  • 简单:一旦你在程序中不一致地包含这样一个头文件(即一次有定义,一次没有它),你就违反了单一定义规则,因此你的程序是错误的(虽然不需要诊断)。换句话说,有效 C++ 程序的属性不再适用于它,因此它实际上可以做任何事情。这还包括在一个示例程序中按预期工作,并在不同程序中以不同方式工作。
  • 这不是一个好的解决方案。你必须在每个包含你的类的文件中定义DEBUG_ENABLED,或者根本没有。您仍在违反 ODR。
  • @GuillaumeRacicot 我知道,我没有提供这个答案作为一个好的解决方案,我知道这些问题,正如我在回答中指出的那样。我仍然认为它是回答部分我的问题是,如果一个文件中的定义包含在包含中。
猜你喜欢
  • 2016-08-09
  • 1970-01-01
  • 2021-02-18
  • 2017-11-08
  • 2021-04-02
  • 2022-12-24
  • 2018-12-17
  • 2013-07-26
  • 1970-01-01
相关资源
最近更新 更多