代码之家  ›  专栏  ›  技术社区  ›  bhups

为什么其他语言不支持类似于预处理器指令的东西,比如C及其后代?

  •  13
  • bhups  · 技术社区  · 15 年前


    #if DEBUG ... #endif . 因此,在发布构建时,这些代码行不会以二进制格式编译。
    但在其他语言中,实现这一点(后面的部分)是困难的(或者可能是不可能的,我不确定)。所有代码都将在二进制文件中编译,以增加其大小。所以我的问题是“你为什么 Java, or other modern compiled languages 不支持这种特性吗?“它允许您以一种非常方便的方式从二进制文件中包含或排除一些代码。

    10 回复  |  直到 15 年前
        1
  •  8
  •   Eli Bendersky    15 年前

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

    有一个文本预处理器,比如 cpp 喜忧参半。自 cpp公司 实际上没有 了解C ,它所做的只是将文本转换为其他文本。这会导致许多维护问题。以C++为例,其中明确地禁止了预处理器的许多用途,以支持更好的特性,例如:

    • const 而不是 #define
    • 对于小功能, inline 而不是 #定义

    C++常见问题解答 calls macros evil

        2
  •  8
  •   Norman Ramsey    15 年前

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

    • 一个功能体变得如此纠结 #ifdef 很难读懂函数,弄清楚发生了什么。请记住,预处理器与 文本 语法 ,所以你可以做一些非常不合语法的事情

    • 代码可以在一个系统的不同分支中复制 #ifdef公司

    • 当一个应用程序要用于多个平台时,编译就变得非常困难 全部的 与为开发人员平台选择的任何代码相反的代码。您可能需要设置多台机器(比方说,在BSD系统上建立一个精确模拟GNU头的交叉编译环境是非常昂贵的。)在大多数Unix都是专有的,供应商必须支持所有Unix的时代,这个问题非常严重。如今,有这么多版本的Unix是免费的,问题就不那么严重了,尽管在Unix环境中复制本机Windows头文件仍然是一项相当具有挑战性的工作。

    • 有些代码被这么多人保护 #ifdef公司 你不知道 -D 需要选项来选择代码。 这个问题是NP困难的,所以最著名的解决方案需要尝试许多不同定义的指数组合。这当然是不切实际的,所以真正的后果是 . 这个问题扼杀了重构,当然,这样的代码完全不受单元测试和回归测试的影响,除非你建立了一个巨大的、多平台的测试场,甚至可能没有。

      在这个领域,我看到这个问题会导致重构应用程序经过仔细的测试和发布,结果立即收到错误报告,而应用程序甚至不会 编译 #ifdef公司

    硬币的另一面是 在预处理器中:

    • 对于一些语言,比如Java, 全部的 依赖于平台的代码在JVM的实现和相关的库中。人们不遗余力地开发独立于平台的JVM和库。

    • 在许多语言中,比如Haskell、Lua、Python、Ruby等等,与C语言相比,设计人员在减少依赖于平台的代码方面遇到了一些麻烦。

    • 在现代语言中,可以将依赖于平台的代码放在编译接口后面的单独编译单元中。许多现代编译器都有很好的跨接口边界内联函数的功能,因此您不必为这种抽象付出太多(或任何)代价。C语言不是这样的,因为(a)没有单独编译的接口;单独的编译模型假设 #include 预处理器;(b)C编译器在拥有64K代码空间和64K数据空间的机器上已经成熟;一个复杂到可以跨模块边界内联的编译器几乎是不可想象的。今天,这样的编译程序已经是例行公事了。一些高级编译器内联和专门化方法 动态 .

    摘要 :通过使用语言机制(而不是文本替换)来隔离依赖于平台的代码,您可以公开 全部的 你的代码到编译器,每件事都得到类型检查,至少,你有机会做一些事情,如静态分析,以确保适当的测试覆盖率。您还排除了一大堆导致代码不可读的编码实践。

        3
  •  6
  •   Billy ONeal IS4    15 年前

    #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.";
        }
    }
    

    您可能会得到相同或至少相似的代码输出。


        4
  •  2
  •   Borealid    15 年前

    其他语言确实支持这个特性,通过使用一个通用的预处理器,比如m4。

        5
  •  2
  •   abelenky    15 年前

    C预处理器可以在 任何 文本文件,不需要是C。

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

        6
  •  2
  •   joe snyder    15 年前

    请注意,宏/预处理/条件/etc通常被视为编译器/解释器功能,而不是语言功能,因为它们通常完全独立于正式的语言定义,并且对于同一种语言,不同的编译器实现可能有所不同。

    $if debug
    array x
    $endif
    ...
    $if debug
    dump x
    $endif

    仅在需要x时声明/分配/编译x,而

    array x
    boolean debug
    ...
    if debug then dump x

    不管debug是否为true,可能都必须声明x。

        7
  •  2
  •   Ferruccio    15 年前

    一个更好的问题是,为什么C使用预处理器来实现这些元编程任务?它不是一个功能,而是对当时技术的妥协。

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

    更现代的语言利用当今功能更强大的机器提供更好的方法来处理前处理器用来处理的各种元编程任务。

        8
  •  1
  •   Jörg W Mittag    15 年前

    非常强大 (实际上是图灵完整的)宏系统,那么他们为什么要把自己局限于蹩脚的CPP风格的宏,而这些宏甚至不是真正的宏,只是文本片段?

    其他具有强大语法元编程的语言包括Io、Ioke、perl6、OMeta和Converge。

        9
  •  0
  •   Assaf Lavie    15 年前

    因为减小二进制文件的大小:

    1. 可以用其他方式来完成(例如,将C++可执行文件的平均大小与C可执行文件进行比较)。
    2. 当你权衡是否能够编写出真正有效的程序时,这一点并不重要。
        10
  •  0
  •   Chris Arguin    15 年前

    其他语言也有更好的动态绑定。例如,由于出口原因,我们有些代码无法发送给某些客户。我们的“C”库使用 #ifdef 语句和精心设计的Makefile技巧(基本相同)。

    Java代码使用插件(alaeclipse),所以我们不需要发布这些代码。

        11
  •  0
  •   12431234123412341234123    5 年前

    另外一点没人提到,就是平台支持。

    大多数现代语言不能运行在与C或C++相同的平台上,也不打算在这个平台上运行。例如,Java、Python和C等本地编译语言都需要一个堆,它们被设计为在具有内存管理、库和大量空间的操作系统上运行,而不是在独立的环境中运行。在那里,您可以使用其他方法来存档相同的文件。C可以用来编程控制器与2kibrom,那里你需要一个预处理器为大多数应用程序。