为什么其他语言不支持类似于C及其后代之类的预处理程序指令的东西?

问题描述 投票:14回答:11

我想知道为什么其他语言不支持此功能。我所了解的C / C ++代码是平台相关的,因此可以通过使用预处理程序指令来使其在各种平台上都能工作(编译和执行)。除此之外,还有许多其他用途。就像您可以将所有调试printf放在#if DEBUG ... #endif中一样。因此,在发布版本时,这些代码行不会在二进制文件中进行编译。但是在其他语言中,很难做到这一点(以后),(我不确定这可能是不可能的)。所有代码都将以二进制形式编译,从而增加其大小。所以我的问题是“为什么Java, or other modern compiled languages不支持这种功能?”这使您可以方便地从二进制文件中包含或排除某些代码。

c programming-languages preprocessor
11个回答
8
投票

没有预处理器的主要语言通常具有不同的,通常更干净的方式来实现相同的效果。

具有像cpp这样的文本预处理器是混合的祝福。由于cpp实际上不是知道C,因此它所做的只是将文本转换为其他文本。这导致许多维护问题。以C ++为例,已明确弃用了预处理器的许多用途,而希望使用更好的功能,例如:

  • 对于常量,用const代替#define
  • 对于小功能,用inline代替#define

C ++ FAQ calls macros evil,并给出避免使用它们的多种原因。


0
投票
Java代码使用插件(ala Eclipse),所以我们只是不发布该代码。

您可以通过使用共享库在C中做同样的事情...但是预处理器要简单得多。


0
投票
大多数现代语言不能在与C或C ++相同的平台上运行,也不能在该平台上运行。例如,Java,Python和C#之类的本地编译语言都需要堆,它们被设计为在具有内存管理,库和大量空间的OS上运行,它们不能在独立的环境中运行。在那里,您可以使用其他方式将其存档。 C可以用于通过2KiB ROM对控制器进行编程,对于大多数应用程序,您需要一个预处理器。

7
投票

预处理程序的可移植性好处远远超过了滥用的可能性。以下是一些我在行业中看到的真实代码的示例:

  • 函数主体变得与#ifdef纠缠在一起,以至于很难读取函数并弄清楚发生了什么。请记住,预处理器使用text而不是syntax,因此您可以执行非常不合语法的操作

  • 代码可能在#ifdef的不同分支中重复,因此很难就所发生的事情保持单一的真相。

  • [当一个应用程序打算用于多个平台时,很难编译all该代码,而不是为开发人员平台选择的任何代码。您可能需要设置多台计算机。 (比方说,在BSD系统上建立一个可以精确模拟GNU标头的交叉编译环境是很昂贵的。)在大多数Unix都是专有的,而供应商必须全力支持它们的时代,这个问题非常严重。如今,当有许多版本的Unix是免费的时,尽管在Unix环境中复制本机Windows标头仍然是很大的挑战,但是问题不大了。

  • [某些代码受到太多#ifdef的保护,所以您无法找出选择-D选项的哪种组合来选择代码。问题是NP难题,因此最知名的解决方案需要以指数方式尝试许多不同的定义组合。这当然是不切实际的,所以真正的结果是逐渐地,您的系统将填充尚未编译的代码。这个问题使重构停滞了下来,当然,这样的代码完全不受单元测试和回归测试的影响,除非您建立了一个庞大的,多平台的测试场,甚至可能还没有。

    在现场,我已经看到此问题导致了对经过重构的应用程序进行仔细测试和交付的情况,只是收到立即的错误报告,表明该应用程序甚至不能在其他平台上进行编译

    。如果代码被#ifdef隐藏并且我们无法选择它,则我们无法保证对其进行类型检查-甚至在语法上都是正确的。
  • 硬币的缺点是更高级的语言和编程技术减少了预处理器中的条件编译需求:] >>

    • 对于某些语言,例如Java,all

    平台相关的代码在JVM的实现中以及相关的库中。人们竭尽全力使JVM和库与平台无关。

  • [[[在许多语言中,例如Haskell,Lua,Python,Ruby等,设计人员在减少与C相比与平台有关的代码量方面遇到了麻烦。

  • 在现代语言中,您可以将平台相关的代码放在已编译接口后面的单独的编译单元中。许多现代的编译器都具有良好的功能,可以跨接口边界内联函数,因此您不必为这种抽象付出太多(或任何代价)。对于C而言并非如此,因为(a)没有单独编译的接口;单独编译模型假定#include和预处理器; (b)C编译器已在具有64K代码空间和64K数据空间的机器上成熟;足够复杂以跨模块边界内联的编译器几乎是不可想象的。如今,此类编译器已成为常规程序。一些高级编译器内联和专门化方法[[dynamically
  • Summary

    :通过使用语言机制(而不是文本替换)来隔离与平台相关的代码,将

    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 ++中,我绝对不会这样做-我会使用预处理器,如果没有其他事情可以立即使我的代码读者清楚地知道其中的大部分不是应该在某些条件下被遵守。我是说,这就是为什么许多语言都避开预处理器的原因。


[其他语言通过使用通用预处理器(例如m4)也支持此功能。
我们真的希望每种语言都具有自己的执行前文本替换吗?

C预处理程序可以在
any文本文件上运行,不必是C。

当然,如果使用另一种语言运行,它可能会以怪异的方式标记化,但是对于#ifdef DEBUG之类的简单块结构,您可以将其放入任何语言中,在其上运行C预处理器,然后运行特定于您的语言的编译器,它将起作用。

请注意,与语言功能相反,宏/预处理/条件/等通常被认为是编译器/解释器功能,因为它们通常完全独立于形式语言定义,并且对于相同的程序,编译器与实现之间可能会有所不同语言。
在许多语言中,条件编译指令可能比if-then-else运行时代码更好的情况是编译时语句(例如变量声明)需要条件的时候。例如

$ 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中的预处理程序指令是在机器资源(CPU速度,RAM)稀缺(且昂贵)的时候开发的。预处理器提供了一种在内存有限的慢速计算机上实现这些功能的方法。例如,我拥有的第一台计算机具有56KB的RAM和2Mhz的CPU。它仍然有完整的K&R C编译器可用,这将系统的资源推到了极限,但是可行。

[更多现代语言利用当今功能更强大的机器来提供更好的方式来处理预处理器用来处理的元编程任务。

由于减小二进制文件的大小:

可以用其他方式完成(例如,将C ++可执行文件的平均大小与C#可执行文件进行比较。)>

    没那么重要,当您权衡它是否能够编写实际有效的程序。
  • 其他语言也具有更好的动态绑定。例如,由于出口原因,我们有一些代码无法交付给某些客户。我们的“ C”库使用#ifdef语句和精心制作的Makefile技巧(几乎相同)。
    Java代码使用插件(ala Eclipse),所以我们只是不发布该代码。

    您可以通过使用共享库在C中做同样的事情...但是预处理器要简单得多。

    没有人提到的另一点是平台支持。
    大多数现代语言不能在与C或C ++相同的平台上运行,也不能在该平台上运行。例如,Java,Python和C#之类的本地编译语言都需要堆,它们被设计为在具有内存管理,库和大量空间的OS上运行,它们不能在独立的环境中运行。在那里,您可以使用其他方式将其存档。 C可以用于通过2KiB ROM对控制器进行编程,对于大多数应用程序,您需要一个预处理器。

  • 6
    投票
    #include <iostream> #define DEBUG int main() { #ifdef DEBUG std::cout << "Debugging..."; #else std::cout << "Not debugging."; #endif }

    2
    投票
    我们真的希望每种语言都具有自己的执行前文本替换吗?

    2
    投票
    any文本文件上运行,不必是C。

    2
    投票
    在许多语言中,条件编译指令可能比if-then-else运行时代码更好的情况是编译时语句(例如变量声明)需要条件的时候。例如

    1
    投票
    extremely strong(实际上是图灵完备的)宏系统,所以为什么要使用它们呢?将自己限制在糟糕的CPP样式宏中,这些宏甚至不是真正的宏,而仅仅是文本片段?

    1
    投票
    C中的预处理程序指令是在机器资源(CPU速度,RAM)稀缺(且昂贵)的时候开发的。预处理器提供了一种在内存有限的慢速计算机上实现这些功能的方法。例如,我拥有的第一台计算机具有56KB的RAM和2Mhz的CPU。它仍然有完整的K&R C编译器可用,这将系统的资源推到了极限,但是可行。

    0
    投票

    可以用其他方式完成(例如,将C ++可执行文件的平均大小与C#可执行文件进行比较。)>

      没那么重要,当您权衡它是否能够编写实际有效的程序。
    © www.soinside.com 2019 - 2024. All rights reserved.