gpt4 book ai didi

c++ - 在代码中使用#ifdef 是一种不好的做法吗?

转载 作者:IT老高 更新时间:2023-10-28 22:17:38 26 4
gpt4 key购买 nike

我必须使用大量的#ifdef i386x86_64 用于特定于体系结构的代码,有时需要使用#ifdef MAC 或#ifdef WIN32... 等等特定于平台的代码代码。

我们必须保持通用代码库和可移植性。

但我们必须遵循#ifdef 的使用是严格不使用的准则。我不明白为什么?

作为这个问题的延伸,我还想了解何时使用 #ifdef ?

例如,dlopen() 在从 64 位进程运行时无法打开 32 位二进制文​​件,反之亦然。因此它的架构更具体。我们可以在这种情况下使用#ifdef 吗?

最佳答案

使用 #ifdef 代替编写可移植代码,您仍在编写多段特定于平台的代码。不幸的是,在许多(大多数?)情况下,您很快就会得到一个几乎难以理解的可移植代码和特定于平台的代码的混合体。

您还经常将 #ifdef 用于可移植性以外的目的(定义要生成的代码的“版本”,例如将包含什么级别的 self 诊断)。不幸的是,两者经常互动,并交织在一起。例如,将一些代码移植到 MacOS 的人认为它需要更好的错误报告,他补充道——但使其特定于 MacOS。后来,其他人认为更好的错误报告在 Windows 上会非常有用,所以他通过自动 #defineing MACOS 如果定义了 WIN32 来启用该代码 - 但随后添加“只是多一些” #ifdef WIN32 在定义 Win32 时排除一些真正 MacOS 特定的代码。当然,我们还添加了 MacOS 基于 BSD Unix 的事实,所以当 MACOS 被定义时,它也会自动定义 BSD_44 —— 但是(再次)转过身来排除一些 BSD 的“东西” "为 MacOS 编译时。

这很快就会退化为如下示例的代码(取自 #ifdef Considered Harmful):

#ifdef SYSLOG
#ifdef BSD_42
openlog("nntpxfer", LOG_PID);
#else
openlog("nntpxfer", LOG_PID, SYSLOG);
#endif
#endif
#ifdef DBM
if (dbminit(HISTORY_FILE) < 0)
{
#ifdef SYSLOG
syslog(LOG_ERR,"couldn’t open history file: %m");
#else
perror("nntpxfer: couldn’t open history file");
#endif
exit(1);
}
#endif
#ifdef NDBM
if ((db = dbm_open(HISTORY_FILE, O_RDONLY, 0)) == NULL)
{
#ifdef SYSLOG
syslog(LOG_ERR,"couldn’t open history file: %m");
#else
perror("nntpxfer: couldn’t open history file");
#endif
exit(1);
}
#endif
if ((server = get_tcp_conn(argv[1],"nntp")) < 0)
{
#ifdef SYSLOG
syslog(LOG_ERR,"could not open socket: %m");
#else
perror("nntpxfer: could not open socket");
#endif
exit(1);
}
if ((rd_fp = fdopen(server,"r")) == (FILE *) 0){
#ifdef SYSLOG
syslog(LOG_ERR,"could not fdopen socket: %m");
#else
perror("nntpxfer: could not fdopen socket");
#endif
exit(1);
}
#ifdef SYSLOG
syslog(LOG_DEBUG,"connected to nntp server at %s", argv[1]);
#endif
#ifdef DEBUG
printf("connected to nntp server at %s\n", argv[1]);
#endif
/*
* ok, at this point we’re connected to the nntp daemon
* at the distant host.
*/

这是一个相当小的例子,只涉及几个宏,但阅读代码已经很痛苦了。我个人看到(并且不得不处理)在实际代码中很多更糟。这里的代码很难看而且读起来很痛苦,但是仍然很容易弄清楚在什么情况下将使用哪些代码。在许多情况下,您最终会得到更复杂的结构。

为了给出一个具体的例子来说明我希望如何写,我会做这样的事情:

if (!open_history(HISTORY_FILE)) {
logerr(LOG_ERR, "couldn't open history file");
exit(1);
}

if ((server = get_nntp_connection(server)) == NULL) {
logerr(LOG_ERR, "couldn't open socket");
exit(1);
}

logerr(LOG_DEBUG, "connected to server %s", argv[1]);

在这种情况下,可能我们对 loggerr 的定义将是一个宏而不是一个实际的函数。这可能是微不足道的,所以有一个像这样的标题是有意义的:

#ifdef SYSLOG
#define logerr(level, msg, ...) /* ... */
#else
enum {LOG_DEBUG, LOG_ERR};
#define logerr(level, msg, ...) /* ... */
#endif

[暂时,假设预处理器可以/将处理可变参数宏]

考虑到你的主管的态度,即使这样可能也是 Not Acceptable 。如果是这样,那很好。取而代之的是宏,而是在函数中实现该功能。将函数的每个实现隔离在其自己的源文件中,并构建适合目标的文件。如果您有很多特定于平台的代码,您通常希望将其隔离到自己的目录中,很可能使用自己的 makefile1,并拥有一个顶级的 makefile 来选择哪个基于指定目标调用的其他 makefile。


  1. 有些人不愿意这样做。我并不是真的在争论如何构建 makefile 的方式,只是指出有些人认为/认为它很有用。

关于c++ - 在代码中使用#ifdef 是一种不好的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8010234/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com