#include <iostream>
#define DEBUG
int main()
{
#ifdef DEBUG
std::cout << "Debugging...";
#else
std::cout << "Not debugging.";
#endif
}
我想知道为什么其他语言不支持此功能。我所了解的C / C ++代码是平台相关的,因此可以通过使用预处理程序指令来使其在各种平台上都能工作(编译和执行)。除此之外,还有许多其他用途。就像您可以将所有调试printf放在#if DEBUG ... #endif
中一样。因此,在发布版本时,这些代码行不会在二进制文件中进行编译。但是在其他语言中,很难做到这一点(以后),(我不确定这可能是不可能的)。所有代码都将以二进制形式编译,从而增加其大小。所以我的问题是“为什么Java, or other modern compiled languages
不支持这种功能?”这使您可以方便地从二进制文件中包含或排除某些代码。
没有预处理器的主要语言通常具有不同的,通常更干净的方式来实现相同的效果。
具有像cpp
这样的文本预处理器是混合的祝福。由于cpp
实际上不是知道C,因此它所做的只是将文本转换为其他文本。这导致许多维护问题。以C ++为例,已明确弃用了预处理器的许多用途,而希望使用更好的功能,例如:
const
代替#define
inline
代替#define
宏C ++ FAQ calls macros evil,并给出避免使用它们的多种原因。
您可以通过使用共享库在C中做同样的事情...但是预处理器要简单得多。
预处理程序的可移植性好处远远超过了滥用的可能性。以下是一些我在行业中看到的真实代码的示例:
函数主体变得与#ifdef
纠缠在一起,以至于很难读取函数并弄清楚发生了什么。请记住,预处理器使用text而不是syntax,因此您可以执行非常不合语法的操作
代码可能在#ifdef
的不同分支中重复,因此很难就所发生的事情保持单一的真相。
[当一个应用程序打算用于多个平台时,很难编译all该代码,而不是为开发人员平台选择的任何代码。您可能需要设置多台计算机。 (比方说,在BSD系统上建立一个可以精确模拟GNU标头的交叉编译环境是很昂贵的。)在大多数Unix都是专有的,而供应商必须全力支持它们的时代,这个问题非常严重。如今,当有许多版本的Unix是免费的时,尽管在Unix环境中复制本机Windows标头仍然是很大的挑战,但是问题不大了。
[某些代码受到太多#ifdef
的保护,所以您无法找出选择-D
选项的哪种组合来选择代码。问题是NP难题,因此最知名的解决方案需要以指数方式尝试许多不同的定义组合。这当然是不切实际的,所以真正的结果是逐渐地,您的系统将填充尚未编译的代码。这个问题使重构停滞了下来,当然,这样的代码完全不受单元测试和回归测试的影响,除非您建立了一个庞大的,多平台的测试场,甚至可能还没有。
在现场,我已经看到此问题导致了对经过重构的应用程序进行仔细测试和交付的情况,只是收到立即的错误报告,表明该应用程序甚至不能在其他平台上进行编译
。如果代码被#ifdef
隐藏并且我们无法选择它,则我们无法保证对其进行类型检查-甚至在语法上都是正确的。硬币的缺点是更高级的语言和编程技术减少了预处理器中的条件编译需求:] >> 对于某些语言,例如Java,all
平台相关的代码在JVM的实现中以及相关的库中。人们竭尽全力使JVM和库与平台无关。
#include
和预处理器; (b)C编译器已在具有64K代码空间和64K数据空间的机器上成熟;足够复杂以跨模块边界内联的编译器几乎是不可想象的。如今,此类编译器已成为常规程序。一些高级编译器内联和专门化方法[[dynamicallySummary
:通过使用语言机制(而不是文本替换)来隔离与平台相关的代码,将all的代码公开给编译器,至少对所有内容进行类型检查,并且进行静态分析之类的机会以确保适当的测试覆盖范围。您还排除了导致不可读代码的大量编码实践。
#include <iostream>
#define DEBUG
int main()
{
#ifdef DEBUG
std::cout << "Debugging...";
#else
std::cout << "Not debugging.";
#endif
}
您可以做:
#include <iostream> const bool debugging = true; int main() { if (debugging) { std::cout << "Debugging..."; } else { std::cout << "Not debugging."; } }
您可能会获得相同或至少相似的代码输出。
编辑/注意:在C和C ++中,我绝对不会这样做-我会使用预处理器,如果没有其他事情可以立即使我的代码读者清楚地知道其中的大部分不是应该在某些条件下被遵守。我是说,这就是为什么许多语言都避开预处理器的原因。
当然,如果使用另一种语言运行,它可能会以怪异的方式标记化,但是对于#ifdef DEBUG之类的简单块结构,您可以将其放入任何语言中,在其上运行C预处理器,然后运行特定于您的语言的编译器,它将起作用。
$ if调试数组x$ endif...$ if调试转储x$ endif
仅在需要x时声明/分配/编译x,而
数组x布尔调试...如果调试,则转储x无论调试是否为真,都可能必须声明x。
[许多现代语言实际上具有超越CPP的语法元编程功能。例如,几乎所有的现代Lips(Arc,Clojure,Common Lisp,Scheme,newLISP,Qi,PLOT,MISC等)都具有extremely strong(实际上是图灵完备的)宏系统,所以为什么要使用它们呢?将自己限制在糟糕的CPP样式宏中,这些宏甚至不是真正的宏,而仅仅是文本片段?具有强大的语法元编程功能的其他语言包括Io,Ioke,Perl 6,OMeta,Converge。
[更多现代语言利用当今功能更强大的机器来提供更好的方式来处理预处理器用来处理的元编程任务。
可以用其他方式完成(例如,将C ++可执行文件的平均大小与C#可执行文件进行比较。)>
#ifdef
语句和精心制作的Makefile技巧(几乎相同)。您可以通过使用共享库在C中做同样的事情...但是预处理器要简单得多。
#include <iostream>
#define DEBUG
int main()
{
#ifdef DEBUG
std::cout << "Debugging...";
#else
std::cout << "Not debugging.";
#endif
}
可以用其他方式完成(例如,将C ++可执行文件的平均大小与C#可执行文件进行比较。)>