一个可行的语言需要预处理器吗?

时间:2010-11-29 22:38:11

标签: c++ c-preprocessor language-design

C ++预处理器真的有用吗?即使在C#中,它仍然具有一些功能,但我一直在考虑完全放弃它用于假设的未来语言。我想有些像Java这样的语言在没有这样的东西的情况下存活下来。没有预处理步骤的语言是否具有竞争力和可行性?用没有预处理器的语言编写的程序采取什么步骤来模拟它的功能,例如调试和发布代码的不同代码,以及它们与#ifdef DEBUG的比较方式?

5 个答案:

答案 0 :(得分:5)

事实上,大多数语言在没有预处理器的情况下处理得非常好。我接着说,在没有多个功能部分的情况下,使用带有C / C ++的预处理器的必要性。

例如:

  • 大多数语言不需要头文件并包含警卫,因为他们有“模块”的概念。
  • 可以通过static if或类似的机制轻松获得条件编译。
  • 代码重复几乎总能以比预处理器更清晰的方式减少:使用模板/泛型,反射系统等等。

所以我的结论是:对于预处理器和元编程可以获得的大多数“功能”,存在更明确的替代方案,使用起来更安全,更方便。

作为一种编译的低级语言,D编程语言是“如何提供通常通过预处理器完成的大多数功能,而无需实际预处理”的一个很好的例子 - 包括我提到的所有内容,以及字符串mixins和模板mixins ,通常可以通过C / C ++中的预处理来解决问题的其他一些聪明的解决方案。

答案 1 :(得分:1)

我会说不,语言不是必需的,因为语言是可行和有竞争力的。

这并不意味着某些语言中不需要宏。

如果您需要宏,可能是因为您的语言存在缺陷。 (或者因为你试图与其他一些不足的语言兼容,比如C ++与C. :))。使语言“足够好”,你将需要很少的语言,语言可以没有它们。

(当然,这取决于你的语言目标是什么“足够好”实际意味着什么,以及宏是否是实现某些事情的好方法,或者仅仅是缺少概念/特征的创可贴。)

即使是在“足够好”的语言中,仍然可能有奇怪的时间,你希望宏在那里。也许他们仍然值得拥有。也许不吧。取决于它们为语言带来了什么以及它们在复杂性(编译器/运行时和程序员)方面引入了哪些问题。但任何语言功能都是如此。你不会设计一种语言,旨在“拥有每一个功能”,所以你必须挑选和选择。选择基于权衡和利益。

e.g。模板在C ++中是一个非常强大的功能,我发现偶尔会在C#中使用它,但是C#应该有它们吗?每种语言都应该有这个功能吗?也许不是,考虑到它们带来的复杂性(事实上你通常可以使用C ++ / CLI进行这种工作)。

顺便说一下,我不是说“任何好语言都没有宏”;我只是说一个好的语言不需要他们。而FWIW,曾经让我觉得Java没有它们,但这可能是因为Java在某些方面缺乏,而不是因为宏是必不可少的。

答案 2 :(得分:1)

非常有用,但应谨慎使用。

您需要它的几个例子。

  1. 目前没有其他标准方法可以正确处理#include其他处理器,因为它是标准的一部分。您还需要define才能包含警卫。但这是C ++特有的问题,在其他语言中不存在。
  2. 当您需要将系统配置为使用不同的API,不同操作系统的不同工具包时,处理器对于条件编译非常有用,这是唯一的方法(除非您想创建抽象接口然后进行条件编译)构建系统级别。)。
  3. 对于没有可变参数模板的当前C ++标准(2003),它在某些情况下使生活更加轻松。例如,当您需要创建一堆类,如:

    template<typename R>
    class function<R()> { ... }
    template<typename R,typename T1>
    class function<R(T1)> { ... }
    template<typename R,typename T1,typename T2>
    class function<R(T1,T2)> { ... }
    ...
    

    如果没有当前C ++标准的处理器,几乎不可能正确地完成它。 (在C ++ 0x中,存在可变模板,使其更容易)。

    事实上,boost::functionboost::signalboost::bind这样的优秀工具需要相当多 复杂的模板处理使这些东西适用于当前的编译器。

  4. 有时模板提供非常好的结构,没有预处理器是不可能的,例如:

    assert(ptr!=0);
    

    该打印件中止程序打印:

      

    断言在foo.cpp中失败,第134行“ptr!= 0”

    当然,它对单元测试非常有用:

    TEST(3.14159 <=pi && pi < 3.141599);
    

    该打印件中止程序打印:

      

    在foo.cpp测试失败,第134行“3.14159&lt; = pi&amp;&amp; pi&lt; 3.141599”

  5. 记录日志。通常使用宏来实现日志记录更容易。为什么呢?

    你需要写:

    if(should_log(info))
       log(info) << "this is the message in file foo.cpp, line 10, foo::doit()" << "Value is " << x;
    

    或更简单:

    LOG_INFO() << "Value is " << x;
    

    其中包括:文件,行号,功能名称和条件。很有价值。

    实际上boost::log apache日志记录使用了这些东西。

  6. 是的......预处理器有时候是邪恶的,但是它太多的情况下它非常有用,所以要巧妙地使用它并且小心,它很好。

    但是如果你用它实现:

    • 宏,而不是内联函数。
    • “减少代码”的宏不可读且不清楚
    • 常数

    你做错了。

    底线

    因为它可以被滥用的每一个工具(相信我,我见过非常疯狂的预处理器 滥用它真正的代码)但今天它是非常有用的东西。

答案 3 :(得分:0)

您不需要预处理步骤来实现条件编译。你真的不需要宏(没有stringize和token-paste操作符就可以生存,甚至可以在没有PP的情况下完成)。 #includes是一种非常特殊的噩梦,它模仿了引用调用的全部错误。

还有什么是如此重要?

答案 4 :(得分:0)

这取决于你认为“可行”的东西,但是 在C ++中,模板,内联和命名空间等功能已经避免了使用宏的许多必要性/可取性。我发现自己在C ++中使用宏的唯一原因是与C集成(标题和处理定义中的#ifdef __cplusplus)。对于其他语言,使用JNI或SWIG等工具生成C头文件/库是不必要的。

不是使用宏来控制'debug'和'nodebug'构建,而是让编译器包含一个调试编译选项以包含/启用必要的功能更有意义。

几种语言在没有宏的情况下工作;如果您正在寻找与C类似的语言进行比较,我建议D http://www.digitalmars.com/d/2.0/comparison.html