Scala 宏的快捷方式

标签 scala scala-2.10 scala-macros

我定义了以下宏来从当前位置获取文件、行和对象/类: http://pastebin.com/UsNLemnK

使用SBT,我定义了两个项目,以便首先编译宏,然后是使用这些宏的实际项目。

这些宏的目的是在日志方法中使用:

def log( msg: Any, srcFile: String = "", srcLine: String = "", srcClass:String = "")

然后我使用此日志方法如下:

log(msg, s"$F_",s"$L_",s"$C_")

其中 F_、L_ 和 C_ 在宏中定义。

现在,我想创建一个快捷方式来避免这个样板文件,只需调用:

log(msg)

应自动替换为

log(msg, s"$F_",s"$L_",s"$C_")

我可以定义一个宏来执行此操作:

def log_(msg: String) : Unit = macro logImpl
def logImpl( c: Context )(msg: c.Expr[String]): c.Expr[Unit] = {
  import c.universe._
  reify( log(msg.splice, srcFile=s"$F_", srcLine=s"$L_", srcClass=s"$C_") )
}

但同样,这个宏需要在项目之前编译,其中定义了日志函数本身...所以我不知道如何解决编译依赖循环...

关于如何做到这一点有什么建议吗? 谢谢

最佳答案

禁止使用 macro annotations (这必然会显着改变您的 API 语法),您必须面对的问题是您需要日志函数的类型检查标识符。

因为您无法导入整个 log实现,解决方案是:

  • 将方法包装到特征中,
  • 在“宏”项目中定义此特征,
  • log_ 添加隐式参数方法,
  • 在您的“主”项目中,创建此特征的实现,并在 implicit val 中实例化此实现。在您想要使用 log_ 的任何地方都可见宏(例如在包对象中)。

当然,您也可以使用简单的 FunctionN在这里并避免特征定义和实现,但这样您就可以避免与其他相同类型的隐式发生潜在的冲突。

一般来说,您的代码将类似于以下内容:

//"macro" project
trait EncapsulatingTrait {
  def yourMethod(...)
}

object Macros {
  def myMacro(...)(implicit param: EncapsulatingTrait) = macro myMacroImpl
  def myMacroImpl( c: Context )(...)
                          (param: c.Expr[EncapsulatingTrait]): c.Expr[...] = {
    import c.universe._
    reify(param.splice.yourMethod(...))
  }
}

//--------------------------
//"main" project
class Impl extends EncapsulatingTrait {
  def yourMethod(...)
}

...

implicit val defaultParam = new Impl

import Macros.myMacro

myMacro(...)

根据您的具体情况,实现方式如下:

//"macro" project
package yourpackage

import java.io.File
import language.experimental.macros
import scala.reflect.macros.Context

trait LogFunction {
  def log( msg: Any, srcFile: String = "", srcLine: Int = -1, srcClass:String = "")
}


object Macros {
      // get current line in source code
      def L_ : Int = macro lineImpl
      def lineImpl( c: Context ): c.Expr[Int] = {
        import c.universe._
        val line = Literal( Constant( c.enclosingPosition.line ) )
        c.Expr[Int]( line )
      }

      // get current file from source code (relative path)
      def F_ : String = macro fileImpl
      def fileImpl( c: Context ): c.Expr[String] = {
        import c.universe._
        val absolute = c.enclosingPosition.source.file.file.toURI
        val base = new File( "." ).toURI
        val path = Literal( Constant( c.enclosingPosition.source.file.file.getName() ) )
        c.Expr[String]( path )
      }

      // get current class/object (a bit sketchy)
      def C_ : String = macro classImpl
      def classImpl( c: Context ): c.Expr[String] = {
        import c.universe._

        val class_ = Literal( Constant( c.enclosingClass.toString.split(" ")( 1 ) ) )  
        c.Expr[String]( class_ )
      }


     def log_(msg: String)(implicit logFunc: LogFunction) : Unit = macro logImpl
     def logImpl( c: Context )(msg: c.Expr[String])(logFunc: c.Expr[LogFunction]): c.Expr[Unit] = {
      import c.universe._
      reify( logFunc.splice.log(msg.splice, srcFile=fileImpl(c).splice, srcLine=lineImpl(c).splice, srcClass=classImpl(c).splice) )
     }
}


//--------------------------
//"main" project
import yourpackage.LogFunction

class LogImpl extends LogFunction {
  def log( msg: Any, srcFile: String = "", srcLine: Int = -1, srcClass:String = "") {
    println(List(msg,srcFile,srcLine,srcClass).mkString("|"))
  }
}

object testLog {

  def main(args: Array[String]): Unit = {

    implicit val defaultLog = new LogImpl

    import yourpackage.Macros.log_

    log_("blah")

  }

}

(请注意,我必须更正 log_ 的签名并稍微调整宏调用)

关于Scala 宏的快捷方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22019910/

相关文章:

scala - 在 Scala 宏中使用 LabelDef (2.10)

scala - Spark 随机森林二元分类器指标

scala - 为什么我不能在扩展通用特征的类中调用参数为 `this` 的方法?

Scala def歧义

apache-spark - 比较spark中两个RDD中的数据

Scala 类型约束来检查参数值

json - 玩转Scala 2.3,JSON Array转Sequence

scala - 如何使用反射实例化 Scala 对象

scala - 为什么 Scala 不推断继承特征的类型成员?

scala - 在父类(super class)中实现宏并在子类中扩展它