我已经定义了以下宏来从当前位置获取文件,行和对象/类: 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_") )
}
但同样,这个宏需要在项目之前编译,其中定义了日志函数本身......所以我没有看到如何解决编译依赖循环......
有关如何执行此操作的任何建议吗? 感谢
答案 0 :(得分:2)
除非使用macro annotations(这必然会显着改变您的API的语法),您必须面对的问题是您需要日志函数的类型检查标识符。
由于您无法导入整个log
实施,解决方案将是:
log_
方法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_
的签名并略微调整宏调用)