如果我想定义一个值,但仅在该值未定义时才这样做,我会像这样处理:
#ifndef THING
#define THING OTHER_THING
#endif
如果THING
是一个typedef
标识符而不是已定义的变量,我该怎么办?我想像这样做:
#ifntypedef thing_type
typedef uint32_t thing_type
#endif
问题出现的原因是我想检查一个外部库是否已经定义了boolean
类型,但我愿意听取更一般的解决方案。
语言本身没有这种东西,也不需要。在一个项目中,你永远不应该有相同的typedef别名引用不同的类型,因为这是ODR的违规行为,如果你要为同一类型创建相同的别名,那就直接这样做就可以了。语言允许你执行任意次数的相同typedef,并且通常会捕获到特定的ODR问题(在同一个翻译单元中):
typedef int myint;
typedef int myint; // OK: myint is still an alias to int
//typedef double myint; // Error: myint already defined as alias to int
如果你想通过使用typedef来确定使用哪个类型来实现不同类型的功能,那么你应该考虑使用模板而不是typedef。C ++没有提供任何机制让代码测试typedef
的存在,您能做到的最好的方式是像这样:
#ifndef THING_TYPE_DEFINED
#define THING_TYPE_DEFINED
typedef uint32_t thing_type
#endif
编辑:
正如@David在评论中所正确指出的那样,这回答了“如何?”的问题,但重要的是错过了“为什么?”的部分。如果你想要做到这一点,可以按照上面的方式进行,但重要的是你可能根本不需要这样做。@David的回答和评论解释了细节,我认为这正确回答了问题。
在C++的预处理阶段中没有这样的功能。最多只能做到:
#ifndef thing_type
#define thing_type uint32_t
#endif
虽然这不是好的编程实践,我不建议这样做。
typedef
创建了一个真正的类型别名,而宏只是纯文本替换。在这个例子中并不重要,但是 void foo(const X x)
的语义取决于 X
是typedef
还是上面的宏定义:typedef int* X
将使函数变为 void foo( int * const )
,而 #define X int*
则会使其变成 void foo( int const * )
(可恶的左边的const
)。 - David Rodríguez - dribeas#define
)是粗糙的文本替换工具,它们对编程语言一无所知,因此不能作用于任何语言级别的定义。#define
一个预处理宏和类型一起使用,使用#ifndef
在定义类型之前检查宏定义。typedef int myInt;
typedef int myInt; // ok, same alias
typedef float myInt; // error
#define DEFAULT_TYPE int // just for argument's sake
#ifndef MY_COOL_TYPE
#define MY_COOL_TYPE DEFAULT_TYPE
#endif
typedef MY_COOL_TYPE My_Cool_Datatype_t;
如果您想自定义类型,则可以在此之前的某个地方(比如在包含在此头文件顶部的“configure”头文件中)定义MY_COOL_TYPE,或者在编译时将其作为命令行参数传递(据我所知,您可以使用GCC和LLVM等编译器进行此操作,也许其他编译器也可以)。
https://msdn.microsoft.com/en-us/library/5xkf423c.aspx?f=255&MSPPError=-2147217396 该语句目前在不可移植,尽管已有要求在GCC中实现它。我认为,在MSVC中也算作“扩展”。这是一个编译器语句,而不是预处理器语句,因此它不会“感知”定义的宏,它只会检测函数体外部的typedef。那里的“full type”表示它将对完整的定义做出反应,忽略像“class SomeClass;”这样的声明。自行决定是否使用。
编辑:显然,它现在还在MacOS上得到支持,并且通过Intel编译器的-fms-dialect标志 (AIX\Linux?)
typedefs
(如bool
)的库与你同样的问题。当它们不关心你用什么来表示bool
或其他库可能会做同样的事情时,这就成为了一个问题!!typedef bool
,然后添加以下代码:#ifdef USE_LIBNAME_BOOL
typedef unsigned char bool; // This is the lib's bool implementation
#else
#include <stdbool.h>
#endif
bool typdef
,则需要包含。这意味着您需要 C99 支持或更高版本。这是一个好问题。C语言和Unix有着悠久的历史,许多Unix C typedef在非POSIX平台(例如Windows,嘘,不要告诉Cygwin的人)上不可用。每当你尝试编写可在这些系统之间移植的C语言时,你都需要决定如何回答这个问题(嘘,不要告诉Cygwin的人)。
如果您需要跨平台可移植性,请了解编译目标的特定于平台的宏定义有时会有所帮助。例如,Windows有定义为_WIN32
的预处理器宏 - 它在编译目标为32位ARM、64位ARM、x86或x64时为1。但是它的存在也告诉我们,我们在Windows机器上。这意味着例如ssize_t
将不可用(不是size_t
)。因此,您可能需要执行类似以下操作:
#ifdef _WIN32
typedef long ssize_t;
#endif
顺便说一下,在这个线程中,人们评论了一个形式上称为Guard(卫兵)的相似模式。在头文件(即接口或“.h”文件)中经常看到它以防止多次包含。你会听到有关头文件保护的内容。
/// @file poop.h
#ifndef POOP_H
#define POOP_H
void* poop(Poop* arg);
#endif
现在我可以在实现文件poop.c
和其他文件(如main.c
)中包含头文件,并且我知道它们将始终成功编译并避免多次包含,无论它们是一起编译还是单独编译,这要感谢头文件保护。
有经验的程序员会使用C++11函数式宏或编程方式来编写头文件保护。如果你喜欢阅读书籍,我推荐Jens Gustedt的《现代C语言》。
如前所述,这不包含在C++标准中,但您可能可以使用autotools来获得相同的功能。
您可以使用{{link1:ac_cxx_bool
}}宏来确保定义了bool(或不同数据类型的不同例程)。
__if_not_exists
这个功能。例如,可以使用以下代码:__if_not_exists( thing_type ) { typedef uint32_t thing_type; }
来定义一个新类型。 - Ise Wisteria