我正在尝试简化大量遗留问题 C 即使在今天,维护人员在进行构建之前也会获取源文件,并在编译之前根据各种类型的环境手动修改以下部分。

示例如下,但问题是这样的。我生锈了 C 但我确实记得不鼓励使用#ifdef。你们能提供更好的选择吗?另外 - 我认为其中一些(如果不是全部)可以设置为环境变量或作为参数传递,如果是这样 - 定义这些然后从源代码访问的好方法是什么?

这是我正在处理的代码片段

#define DAN          NO
#define UNIX         NO
#define LINUX        YES
#define WINDOWS_ES   NO
#define WINDOWS_RB   NO

/* Later in the code */
#if ((DAN==1) || (UNIX==YES))
#include <sys/param.h>
#endif

#if ((WINDOWS_ES==YES) || (WINDOWS_RB==YES) || (WINDOWS_TIES==YES))
#include <param.h>
#include <io.h>
#include <ctype.h>
#endif

/* And totally insane harcoded paths */
#if (DAN==YES)
char MasterSkipFile[MAXSTR] = "/home/dp120728/tools/testarea/test/MasterSkipFile";
#endif

#if (UNIX==YES)
char MasterSkipFile[MAXSTR] = "/home/tregrp/tre1/tretools/MasterSkipFile";
#endif

#if (LINUX==YES)
char MasterSkipFile[MAXSTR] = "/ptehome/tregrp/tre1/tretools/MasterSkipFile";
#endif

/* So on for every platform and combination */
有帮助吗?

解决方案

当然,您可以在命令行上传递 -DWHATEVER 。或者 -DWHATEVER_ELSE = NO 等。也许你可以做一些像

这样的路径
char MasterSkipFile[MAXSTR] = SOME_COMMAND_LINE_DEFINITION;

然后传递

-DSOME_COMMAND_LINE_DEFINITION="/home/whatever/directory/filename"

在命令行上。

其他提示

我们过去做的一件事是生成带有这些定义的 .h 文件,并使用脚本生成它。这帮助我们摆脱了许多脆弱的#ifs和#ifdefs

你需要注意你放在那里的东西,但机器特定的参数是很好的选择 - 这就是autoconf / automake的工作方式。

编辑:在你的情况下,一个例子是使用生成的 .h 文件来定义 INCLUDE_SYS_PARAM INCLUDE_PARAM ,并在代码本身使用:

#ifdef INCLUDE_SYS_PARAM
#include <sys/param.h>
#endif

#ifdef INCLUDE_PARAM
#include <param.h>
#endif

使移植到新平台变得更加容易 - 新平台的存在不会渗透到代码中,只会导致生成的 .h 文件。

平台特定的配置标头

我有一个系统可以将特定于平台的配置生成到所有构建中使用的标头中。AutoConf 名称为“config.h”;您可以看到“platform.h”或“porting.h”或“port.h”或主题的其他变体。该文件包含正在构建的平台所需的信息。您可以通过将版本控制的特定于平台的变体复制到标准名称来生成该文件。您可以使用链接而不是复制。或者,您可以运行配置脚本,根据脚本在计算机上找到的内容来确定其内容。

配置参数的默认值

代码:

#if (DAN==YES)
char MasterSkipFile[MAXSTR] = "/home/dp120728/tools/testarea/MasterSkipFile";
#endif

#if (UNIX==YES)
char MasterSkipFile[MAXSTR] = "/home/tregrp/tre1/tretools/MasterSkipFile";
#endif

#if (LINUX==YES)
char MasterSkipFile[MAXSTR] = "/ptehome/tregrp/tre1/tretools/MasterSkipFile";
#endif

最好替换为:

#ifndef MASTER_SKIP_FILE_PATH
#define MASTER_SKIP_FILE_PATH "/opt/tretools/MasterSkipFile"
#endif

const char MasterSkipFile[] = MASTER_SKIP_FILE_PATH;

那些想要在不同位置构建的人可以通过以下方式设置位置:

-DMASTER_SKIP_FILE_PATH='"/ptehome/tregtp/tre1/tretools/PinkElephant"'

注意单引号和双引号的使用;尽量避免在路径中带有反斜杠的命令行上执行此操作。您可以对各种事情使用类似的默认机制:

#ifndef DEFAULTABLE_PARAMETER
#define DEFAULTABLE_PARAMETER default_value
#endif

如果你选择好默认值,这可以节省大量能源。

可重定位软件

我不确定只能安装在一个位置的软件的设计。在我的书中,你需要能够在机器上同时安装产品的旧版本1.12和新的2.1版本,并且它们应该能够独立运行。硬编码的路径名可以克服这一点。

按功能而不是平台进行参数化

AutoConf 工具与一般替代系统之间的主要区别在于,配置是基于功能而不是平台完成的。您可以对代码进行参数化以识别您想要使用的功能。这一点至关重要,因为功能往往会出现在原始平台以外的平台上。我查看有以下行的代码:

#if defined(SUN4) || defined(SOLARIS_2) || defined(HP_UX) || \
    defined(LINUX) || defined(PYRAMID) || defined(SEQUENT) || \
    defined(SEQUENT40) || defined(NCR) ...
#include <sys/types.h>
#endif

拥有以下内容会好得多:

#ifdef INCLUDE_SYS_TYPES_H
#include <sys/types.h>
#endif

然后在需要的平台上生成:

#define INCLUDE_SYS_TYPES_H

(不要太从字面上理解这个示例标题;这是我试图克服的概念。)

将平台视为一组功能

作为上一点的推论,您确实需要检测平台并定义适用于该平台的功能。这是定义配置功能的特定于平台的配置标头的位置。

应在标题中启用产品功能

(详细阐述我对另一个答案的评论。)

假设您的产品中有很多功能需要有条件地包含或排除。例如:

KVLOCKING
B1SECURITY
C2SECURITY
DYNAMICLOCKS

设置适当的定义后,将包含相关代码:

#ifdef KVLOCKING
...KVLOCKING stuff...
#else
...non-KVLOCKING stuff...
#endif

如果您使用像这样的源代码分析工具 范围, ,那么如果它可以显示 KVLOCKING 何时定义,将会很有帮助。如果定义它的唯一位置是分散在构建系统周围的一些随机 Makefile 中(我们假设其中使用了一百个子目录),则很难判断该代码是否仍在其中的任何一个上使用。您的平台。如果定义位于某个标头中 - 特定于平台的标头,或者可能是产品发布标头(因此版本 1.x 可以具有 KVLOCKING,版本 2.x 可以包含 C2SECURITY,但 2.5 包含 B1SECURITY 等),那么您可以看到KVLOCKING 代码仍在使用。

相信我,经过二十年的发展和人员流动,人们不知道功能是否仍在使用(因为它很稳定并且永远不会引起问题 - 可能是因为它从未被使用过)。如果查找 KVLOCKING 是否仍然定义的唯一位置是在 Makefile 中,那么像 cscope 这样的工具就没那么有用了——这使得在稍后尝试清理时修改代码更容易出错。

使用它更加合理:

#if SOMETHING

..从平台到平台,避免混淆破碎的预处理器。但是,任何现代编译器最终都应该有效地论证你的情况。如果您在平台,编译器和预处理器上提供更多详细信息,您可能会收到更简洁的答案。

条件编译,鉴于其中过多的操作系统和变体是必要的邪恶。如果,ifdef等最明显滥用预处理器,只是按预期行使它。

我首选的方法是让构建系统执行操作系统检测。您希望将特定于机器的内容隔离到单个源文件中的复杂案例,并为不同的操作系统提供完全不同的源文件。

所以在这种情况下,你在该文件中有一个 #include&quot; OS_Specific.h&quot; 。您为此平台添加了不同的包含和MasterSkipFile的定义。您可以通过在编译器命令行中指定不同的 -I (包括路径目录)来在它们之间进行选择。

这样做的好处是,有人试图找出代码(可能是调试)并不需要为他们甚至没有运行的平台的幻像代码(并且可能被误导)

我见过构建系统,其中大多数源文件都是这样开始的:

#include PLATFORM_CONFIG
#include BUILD_CONFIG

并且编译器开始于:

cc -DPLATFORM_CONFIG="linuxconfig.h" -DBUILD_CONFIG="importonlyconfig.h"

(这可能需要反斜杠转义)

这样可以让您将一组文件中的平台设置与另一组文件中的配置设置分开。平台设置管理处理可能不存在于一个平台上的库调用,或者不正确格式,以及定义重要的依赖于大小的类型 - 特定于平台的东西。构建设置处理输出中启用的功能。

普通

我是一位从GNU Autotools教堂被赶出的异教徒。为什么?因为我想了解我的工具到底在做什么。而且因为我有尝试组合两个组件的经验,每个组件都坚持使用不同的,不兼容的autotools版本作为我计算机上安装的默认版本。

我通过为平台和重要抽象的每个组合创建一个.h文件或.c文件来工作。我努力定义一个中心.h文件,说明接口是什么。通常这意味着我最终创建了一个“兼容层”。这使我免受平台之间的差异的影响。我总是尽可能使用ANSI标准C,而不是特定于平台的功能。

我有时会编写脚本来生成与平台相关的文件。但是脚本总是手工编写并记录下来,所以我知道他们做了什么。

我很欣赏Glenn Fowler的 nmake 和Phong Vo的 iffe (如果功能存在),我认为它比GNU工具更好。但是这些工具是AT&amp; T软件技术套件的一部分,而且我还没有弄清楚如何使用它们而不购买整个AST的做事方式,我并不总是理解。

你的例子

显然需要

extern char MasterSkipFile[];

在某个.h文件中,然后您可以链接到合适的.o。

有条件地包含“用于平台的正确的.h文件集”。我会在可能的情况下尝试坚持使用ANSI C,并且在不可能的情况下,在特定于平台的.h文件中定义兼容层。事实上,我无法分辨 #include s试图导入的名称,因此我无法提供更具体的建议。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top