C pre-processor 或 C++ 魔术自动为每个文件创建一个 object?

C pre-processor or C++ magic to automatically make an object per file?

我有一个函数,其行为可能需要根据调用它的文件进行修改(例如,增加调试跟踪输出)。这种修改需要在不重新编译的情况下完成(例如,通过编辑配置文件或更改环境变量)。

作为满足此需求的示例,我可以将 Function() 写为:

FunctionModifiable( const char* pszFile, int i );

然后做一个宏:

#define Function( i ) FunctionModifiable( __FILE__, (i) )

并且 FunctionModifiable() 将有责任检查 pszFile,比如在初始化期间填充的 unordered_set<>,以查看是否需要激活特殊功能。

但是,该搜索的开销是一个负数(这是 high-performance 软件并且该函数被调用了很多次),并且有一些 per-file 数据需要在这种情况下被缓存。我们可以消除搜索,并获得缓存信息的存储空间,方法是传入一个指向助手 object 的指针而不是 __FILE__。这个 object 需要文件名,这样当它进行 one-off 初始化时,它可以查询配置或环境变量或者你知道它是否需要特殊处理。

FunctionHelperObject fho( __FILE__ );

#define Function( i ) FunctionModifiable( &fho, (i) )  // C-style solution
#define Function( i ) fho.MethodModifiable( (i) )        // C++-style solution

好的,现在说我想避免用户必须在每个文件中定义 fho。 (除其他外,我们不能 re-write 所有现有文件调用 Function(),尽管我们愿意重新编译它们)。

我的想法是在 header 文件 中放置一个变量定义的不寻常步骤,这样任何包含 header 的程序都可以用于 Function() 将免费获得 FunctionHelperObject fho( __FILE__ )。 (这样的定义将是 #pragma once 或由预处理器变量保护。

问题是 __FILE__ 在那个时候将是 header 的名称,而不是 top-level 编译单元的名称。如果有一个 __CFILE__ 符号,那将是解决方案,但没有。

最终,我能想到的最好的方法有缺点:1) "modifiable" 方面只能在明确编写的源代码中使用它,以及 2) 你必须进行明确的编写, 3)开始变得有点复杂。在代码中,您想添加修改行为的能力,以便在包含有问题的 header 之后在某处写入 USE_MODIFIABLE_FUNCTION 。这将是一个创建上面 FunctionHelperObject 的宏,这次在右边 "file" 所以 __FILE__ 将具有所需的值,并且还定义了一个如上所示的宏,它将掩盖non-customizable 函数 Function() 与上面看到的两个宏之一。简而言之:前面的例子,加上

#define USE_MODIFIABLE_FUNCTION FunctionHelperObject fho( __FILE__ );\n#define Function( i ) fho.MethodModifiable( (i) )

没有 USE_MODIFIABLE_FUNCTION 编写的代码将简单地以正常方式调用不可自定义的 Function()

但这肯定是提供此行为的其他公认的可移植方式吗?虽然我只讨论了 C 预处理器,但是否有任何 C++ 模板魔术或任何其他可行的方法?

一个选项是这样的(粗略的例子,保留 OP 中的 C++ 语法):

#define Function(i) do { \
    static FunctionHelperObject obj(__FILE__); \
    FunctionModifiable(&obj, (i)); \
} while (0)

请注意,如果函数有 return 值,则必须修改上述内容以适应 return 值。

另外:__FILE__ 的替代方案可能是 __func__ 如果它更符合您的需求。

缓存结果。

// in the header with Function macro
static FunctionHelperObject functionhelper;
static inline void FunctionModifiableInterface(const char *file, int i) {
    static initialized = 0;
    if (initialized == 0) {
        initialized = 1;
        functionhelper = FunctionHelperObject(file);
    }
    FunctionModifiable(&functionhelper, i);
}
#define Function(i) FunctionModifiableInterface(__FILE__, (i))

您无法预测用户想在何处给您打电话 Function(i),因此您无法预测 __FILE__ 的值。只需在第一次调用时初始化它,这也很好,因为如果 Function 未被调用,您将不会初始化它。您可以在 FunctionHelperObject 构造函数中执行相同的 initialized 检查。

真正酷又难做的技巧是修改你的构建系统,允许你传递一个带有已编译 C 文件文件名的宏。因为构建系统一次编译一个 C 文件,所以这是可能的(遗憾的是编译器自己不这样做)。如果你使用 cmakemake 后端(或者实际上只是 make 本身),你可以做一些事情 like this:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")

然后像你想的那样使用 FunctionHelperObject fho(__MY_FILE__),因为 __MY_FILE__ 只依赖于 make.

的输出文件名