如何在 C++ 的 #define 中引用函数原型参数默认值?

How can I reference function prototype parameter default values in a #define in C++?

提问人:Rick C. Hodgin 提问时间:6/17/2023 最后编辑:Rick C. Hodgin 更新时间:6/17/2023 访问量:92

问:

假设我有一个函数原型,它默认了几个参数:

bool debug_log(char* text, int len,
               bool   log_always = true,    // Defaults to yes, log
               SRgba* backRgba   = NULL,    // Use default color
               SRgba* foreRgba   = NULL);   // Use default color

如果我创建了一些引用该函数,但为其日志记录类型填充了颜色值,我如何根据该参数在原型中的定义方式获得该参数的默认值?#defineslog_always

#define debug_log_err(a, b) debug_log(a, b, \
                                      true, \
                                      &errBackRgba, \
                                      &errForeRgba)
    
#define debug_log_block(a, b) debug_log(a, b, \
                                        true, \
                                        &blockBackRgba, \
                                        &blockForeRgba)

#define debug_log_highlight(a, b) debug_log(a, b, \
                                            true, \
                                            &highlightBackRgba, \
                                            &highlightForeRgba)

在这些情况下,目前一切都是同步的,因为第三个参数正在传入,这是原型定义默认参数的方式。但是,如果我后来进入并决定将log_always设置为 ,那么除非我记得也更新我的 #define 用法,否则它们将过时且不同步。truefalse

我想要某种方式来使用 @ 字符之类的东西来做这样的事情,它告诉编译器使用原型的任何默认值 就在那里,而不是我必须对它进行硬编码。

#define debug_log_err(a, b) debug_log(a, b, \
/* Let the compiler fill in */        @, \
                                      &errBackRgba, \
                                      &errForeRgba)

#define debug_log_block(a, b) debug_log(a, b, \
/* Let the compiler fill in */          @, \
                                        &blockBackRgba, \
                                        &blockForeRgba)

#define debug_log_highlight(a, b) debug_log(a, b, \
/* Let the compiler fill in */              @, \
                                            &highlightBackRgba, \
                                            &highlightForeRgba)

这样的能力存在吗?如果没有,这似乎是 C++ 中的一个缺点,类似于无法只指定几个参数,并在提供时让其余参数自动填充其默认值。

更新:我得到的是,像这样的扩展到他们扩展到的任何内容,然后进行编译。所以也许我要问的问题是......有没有办法在函数中使用干预默认参数进行引用,我知道答案是否定的。所以,这个问题主要是评论,我认为应该改变,并且应该将能力添加到 C++ 中。#define

C++ C-预处理器 默认参数

评论

6赞 Pepijn Kramer 6/17/2023
不要,只是不要为此使用宏。只需制作函数或函数模板即可。在这里阅读更多: 为什么预处理器宏是邪恶的。标题有点戏剧化,但请阅读答案。
3赞 Nate Eldredge 6/17/2023
请记住,宏是愚蠢的(从所有意义上讲)。他们只是做简单的文本替换,仅此而已;他们不了解该文本的语义(它对程序的实际含义)。它们没有任何类型、默认参数、模板或任何接近该级别的概念。
0赞 Ripi2 6/17/2023
更改顺序,将log_always放在末尾。现在你可以使用#define debug_log_err(a, b, c) debug_log(a, b, &errBackRgba, &errForeRgba, c)
0赞 Rick C. Hodgin 6/17/2023
Ripi2 ,如果默认情况下颜色参数不再是 NULL,而是在代码中的某个地方使用,则对颜色参数的任何引用都会存在同样的问题,因为需要重写log_always值。我真的认为 C++ 需要添加允许编译器在可能的情况下拉取默认原型值的能力。
0赞 Ripi2 6/17/2023
如果您有这么多可能的组合,为什么要使用宏?只需使用定义的函数,而不是某些宏的“短版本”。

答:

2赞 rgnt 6/17/2023 #1

这样的能力存在吗?如果没有,这似乎是 C++ 中的一个缺点,类似于无法只指定几个参数,并在提供时让其余参数自动填充其默认值。

编译器无法猜测您提供的参数的位置,这就是为什么可选参数必须排在最后的原因。这是惯例。

当然,你的观点是添加一个运算符,让编译器知道你正在尝试替换函数原型提供的默认值,但编译器实际上不可能知道你试图调用什么函数


// the operator for argument substitution is @

int log(int level, bool flag = false, int color = 0);
int log(int level, int color = 0);

int main() {
    log(0, @, 1); // Obviously, there's only one function, so this call is clear
    log(0, @);    // But considering overloading, this call is ambiguous 
    return 0;
}

因此,您可以看到操作员在哪些方面存在不足,并且在链接时可能导致微不足道的问题。

至于解决方案,为什么不直接将参数移到后面呢?log_always

bool debug_log(char* text, int len,
               SRgba* backRgba   = NULL,    // Use default color
               SRgba* foreRgba   = NULL,    // Use default color
               bool   log_always = true,    // Defaults to yes, log
);   

#define debug_log_err(a, b) debug_log(a, b, \
                                      &errBackRgba, \
                                      &errForeRgba, \
                                      true)
    
#define debug_log_block(a, b) debug_log(a, b, \
                                        &blockBackRgba, \
                                        &blockForeRgba)

#define debug_log_highlight(a, b) debug_log(a, b, \
                                            &highlightBackRgba, \
                                            &highlightForeRgba)

评论

0赞 Rick C. Hodgin 6/17/2023
如果我想覆盖并使用......它要求 NULL 和 NULL 是硬编码的用途,我不希望(因为它们以后也可能在函数原型中更改)仅仅因为我需要更改后面的参数而必须填充。我认为可以引入@字符来保存默认参数(如果可用)。在调用不明确的情况下,生成错误。我的 0.02 美元。:-)debug_log("hi", 2, NULL, NULL, false);
0赞 rgnt 6/17/2023
对此,我说,有多个重载,一个只定义颜色,一个只定义标志,最后一个包含实现并具有所需的所有参数
0赞 Rick C. Hodgin 6/17/2023
感谢您的反馈。我是一名编译器开发人员,我认为这个问题可以解决。我还相信在预编译时扩展任何宏以生成新的源文件,该文件是在编译和调试期间使用的源文件,它允许在每次迭代时在附近的注释代码中看到完整的扩展。我认为我们不需要将自己局限于今天标准所说的内容,但是如果考虑到我们庞大的遗留代码库,某些东西是有意义的,那么它应该被实现并添加到标准中。再次是我的 0.02 美元或 0.01 美元。:-)
3赞 Eugene 6/17/2023 #2

要解决您的问题,请将函数原型和宏替换为常量:true

    const bool log_always_default = true;  // Defaults to yes, log

    bool debug_log(char* text, int len,
                   bool   log_always = log_always_default,    
                   SRgba* backRgba   = NULL,    // Use default color
                   SRgba* foreRgba   = NULL);   // Use default color

    #define debug_log_err(a, b) debug_log(a, b, \
                                          log_always_default, \
                                          &errBackRgba, \
                                          &errForeRgba)
        
    #define debug_log_block(a, b) debug_log(a, b, \
                                            log_always_default, \
                                            &blockBackRgba, \
                                            &blockForeRgba)
    
    #define debug_log_highlight(a, b) debug_log(a, b, \
                                                log_always_default, \
                                                &highlightBackRgba, \
                                                &highlightForeRgba)

现在,如果您更改为 ,它将影响您的所有宏。log_always_defaultfalse

正如其他人指出的那样,没有理由在这里使用宏。您可以将它们替换为以下函数:inline

inline bool debug_log_err(char* text, int len)
{
    return debug_log(text, len, log_always_default, &errBackRgba, &errForeRgba);
}