【问题标题】:What's the point of a PROTOTYPE macro that merely expands to its arguments?仅扩展为参数的 PROTOTYPE 宏有什么意义?
【发布时间】:2020-01-06 19:12:15
【问题描述】:

我有一个头文件,其中包含

#define PROTOTYPE(s) s

这有什么意义?似乎它只会用自己替换输入。

它周围还有很多其他指令,但唯一一个似乎有任何意义的指令只是检查了它是否已定义:#ifndef PROTOTYPE。我在 HDF4 头文件中发现了一些这样做的地方:#define PROTOTYPE。所以,这些都没有真正解决我的问题。看起来还是没什么用。

它的使用方法如下:

CS_RETCODE clientmsg_callback PROTOTYPE((
CS_CONTEXT * context,
CS_CONNECTION *connection,
CS_CLIENTMSG *clientmsg));

这是使用 Sybase Open Client 的项目的一部分。后面会用到clientmsg_callback:

ct_callback(context, NULL, CS_SET, CS_CLIENTMSG_CB,
                  (CS_VOID *)clientmsg_callback);

我要离开这里的示例程序:

http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc35570.1570/html/clcprgde/clcprgde10.htm

clientmsg_callback 稍后实现。我认为该示例最初是用 C 编写的,而不是 C++。也许这与它有关?

【问题讨论】:

  • 附近是否有 #if / #ifdef / #ifndef / #else 指令可能有不同的定义?在其他宏中使用时可能会有所不同,尤其是在### 附近。它可能只是一种评论风格。没有足够的上下文来真正回答。
  • 作为一般答案:因为有人可能有理由想要更改PROTOTYPE。如果您在代码中看到看似无用的奇怪定义,请考虑如果有人想方便地更改某些内容,则可能具有灵活性。

标签: c++ macros


【解决方案1】:

在非常非常早期的 C 语言的过去,没有原型这样的东西。函数参数列表位于函数括号之后,like this:

square(x)
int x;
{
int y = x * x;
return y;
}

当然,这些天来,参数放在括号内:

square(int x)
{
int y = x * x;
return y;
}

注意“缺失”的返回类型; C 函数过去会隐式返回 int,只有当您需要不同的返回类型时,您才必须说出它是什么。

函数声明还有另一套规则。 K&R C(古代版本)中的函数声明没有参数:

int square();

ANSI C 中的函数 prototypes 有一个参数列表:

int square(int x);

在过渡期间,人们使用古怪的宏,因此他们可以两种方式进行编译:

int square(PROTOTYPE(int x));

#define PROTOTYPE(s)

这将扩展到第一个版本。

#define PROTOTYPE(s) s

它会扩展到第二个。

关于问题代码中的“额外”括号,当参数列表中有多个参数时需要它们。没有它们,宏调用有多个参数,因此不会匹配只用一个参数定义的宏:

PROTOTYPE(int x, int y)   // error: too many arguments
PROTOTYPE((int x, int y)) // ok: only one argument (enclosed in parentheses)

【讨论】:

  • 哇。过去的爆炸。我在软件方面的第一份工作就是从现有的代码库中剥离这些东西。对 Unix 的一系列单行文本修改程序建立了良好的尊重。
  • 我不明白这些定义是如何工作的,#define PROTOTYPE(s) 与输入 int x; 是如何变成 x 的?对我来说,它看起来像是一个空字符串
  • @Ferrybig -- 抱歉,我把事情弄糊涂了。正是以这种方式定义的原型。在 K&R C 中,原型没有参数,而在 ANSI C 中,它具有我们习惯看到的参数列表样式。
  • 这种做法也可以在 zlib 库的 zlib.h 标头中看到,带有 OF() 宏:github.com/madler/zlib/blob/master/zlib.h
  • @PaulBelanger 实际定义在zconf.h
【解决方案2】:

这样的宏将在头文件的原型中使用以允许这样的事情:

int foo PROTOTYPE((int bar));

如果检测到 ANSI C(__STDC__ 定义为 1),这将扩展为:

int foo(int bar);

如果未检测到 ANSI C,这将扩展为:

int foo();

这在 C 标准化之前很常见。

有些图书馆仍然这样做;如果您查看tcpd.h(如果有的话),您会看到:

/* someone else may have defined this */
#undef  __P

/* use prototypes if we have an ANSI C compiler or are using C++ */
#if defined(__STDC__) || defined(__cplusplus)
#define __P(args)       args
#else
#define __P(args)       ()
#endif

这很好解释。

至于双括号,__P(arg1, arg2) 会给出语法错误(向宏传递太多参数),而__P((arg1, arg2)) 会很好(括号中只有一个)。

这类似于 GNU C 中的 __extension__((...))。在非 GNU 编译器中,只需 #define __extension__(unused) 即可获得半可移植代码,因为只给出了一个“参数”,并用括号括起来。

【讨论】:

    猜你喜欢
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多