Scala宏的快捷方式

时间:2014-02-25 16:14:51

标签: 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_") )
}

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

有关如何执行此操作的任何建议吗? 感谢

1 个答案:

答案 0 :(得分:2)

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

由于您无法导入整个log实施,解决方案将是:

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

当然,你也可以在这里使用一个简单的FunctionN并避免特征定义和实现,但这样你就可以避免与其他相同类型的隐含冲突。 < / p>

通常,您的代码类似于以下内容:

//"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_的签名并略微调整宏调用)

相关问题