C ++中的预处理器及其在Scala中的替代方法

时间:2015-04-24 13:45:22

标签: scala macros preprocessor

预处理器指令的Scala中的替代实现是什么,比如在C ++中?假设我有类似的东西:

 #ifdef ADD
 class Add extends Expr {
   Expr left , right ;
   Add ( Expr l, Expr r)
     { left =l; right =r; }
 #ifdef EVAL
   double eval () {
     return left.eval () +
       right.eval ();
   }
 #endif
 #ifdef PRINT
   void print () {
       left.print ();
         System.out.print("+");
         right.print();
   }
 #endif
 }
 #endif

我如何在Scala中拥有相同的功能?

2 个答案:

答案 0 :(得分:2)

如果未定义相应的常量,则C / C ++预处理器不会为ifdef块中的代码发出任何内容。 Scala没有预处理器,据我所知,从字节代码生成中排除Scala代码的唯一方法是从构建中排除代码。

您可以使用逻辑来定义ADD以控制Add.scala是否是构建的一部分,从而可以使用sbt。

EVAL和PRINT常量必须以类似方式处理,每个文件都有一个文件,并使用用于添加方法的相同技巧使Add类上的方法可用。双倍或长。我真的不知道是否可以将RichC1和RichC2组合成一个单独的类,这本身就是一个有趣的问题。

无论如何,咳嗽几乎肯定会导致一团糟咳嗽。唯一的好处是代码大小的无限减少。如果这种减少很重要,那么Scala不太可能是最合适的工具。简单地删除ifdef并包含构建中的所有代码可能会更好。

答案 1 :(得分:1)

首先,你必须问自己,"为什么我要有条件地包含影响许多对象的代码片段?" (扩大我的评论)

传统上,预处理控制代码用于

  1. 功能配置控制 - 构建中包含哪些功能。这部分是您定义的界面,但主要是您使用和调用功能集的方式。
  2. 架构控制 - 软件运行的环境,例如操作系统和处理器架构。
  3. 注释和横切关注点 - 调试,断言,日志记录,约束检查等。
  4. 对于第一项,功能配置控制,非常重要的是要注意使用预处理器方法,虽然在小规模方便,但实际上往往会使软件变得模糊,并且可能导致你失去兔子洞。

    Scala方法是定义一组称为traits的接口,然后将这些接口组合成一个完整的图片。如果您确实需要在代码中不包含特定组件,那么您只需要创建一组包含所需代码的类,并将它们打包到模块(包或jar)中。然后将该模块包含在最终包装中。

    第二个问题以类似的方式解决。您可以为公共代码创建包,并为平台特定项创建包。然后,在运行时,您可以动态选择要加载的包。这可以通过显式包路径来完成,或者如果你想对它感到棘手,你可以尝试创建一个类加载器来为你做脏工作。然而,从长远来看,事先做好事情会更好。

    Scala中的几种方法解决了第三个问题。

    Java和Scala中使用的方法是注释。它们使用程序化标记方法来指示特定类,方法或字段具有特殊处理。请参阅Scala documentation。但是,使用它来标记方法不存在并不是一个好用途,即使它是可能的。例如,@deprecated注释用于告诉程序员何时API元素消失,并允许编译器或IDE标记应升级的项目。

    Scala中可用的另一种方法是使用宏。从this page开始,我们有了模糊:宏适用于代码生成,静态检查和特定于域的语言。从理论上讲,您可以将它们用于您尝试进行预处理的内容。

    一般来说,预处理方法会导致厨房水槽"类型编程。另一个难以处理的问题是,你的方法需要应用程序中的每个模块,无论是库还是主程序,都需要使用相同的标志进行编译。这是一个巨大的问题。制作系统的负担,特别是一旦你传递了数百个源文件标记。