Execute splice() for varargs in macro method in Scala 2.13 - scala

I want to execute splice() for each argument of my varargs:
import scala.reflect.macros.blackbox
object LoggerMacro {
def log(context: blackbox.Context)
(message: context.Expr[String], arguments: context.Expr[Any]*)
: context.universe.Expr[Unit] = context.universe.reify {
println(message.splice) // Works :)
for (argument <- arguments) {
println(argument.splice) // Fails :(
}
}
}
However, I receive the following error message:
LoggerMacro.scala:9:24
the splice cannot be resolved statically, which means there is a cross-stage evaluation involved.
cross-stage evaluations need to be invoked explicitly, so we're showing you this error.
if you're sure this is not an oversight, add scala-compiler.jar to the classpath,
import `scala.tools.reflect.Eval` and call `<your expr>.eval` instead.
println(argument.splice)
Unfortunately, when I add scala-compiler as dependency and import scala.tools.reflect.Eval, there is still no callable eval method on my expr argument.
How can I access my arguments receiving as varargs?

In Scala 2 it's easier to work with Trees q"..." (and splicing like $, ..$, ...$) rather than Exprs (and .splice). So try
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
object Macro {
def logMacro(message: String, arguments: Any*): Unit = macro log
def log(c: blackbox.Context)(message: c.Tree, arguments: c.Tree*): c.Tree = {
import c.universe._
q"""
_root_.scala.Predef.println($message)
for (argument <- _root_.scala.collection.immutable.Seq.apply(..$arguments)) {
_root_.scala.Predef.println(argument)
}
"""
}
}
Macro.logMacro("a", 1, 2, 3)
// scalacOptions += "-Ymacro-debug-lite"
//{
// _root_.scala.Predef.println("a");
// _root_.scala.collection.immutable.Seq.apply(1, 2, 3).foreach(((argument) => _root_.scala.Predef.println(argument)))
//}
//a
//1
//2
//3
But if you prefer Exprs/.splice then you can write yourself a helper transforming Seq[Expr[A]] into Expr[Seq[A]].
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
object Macro {
def logMacro(message: String, arguments: Any*): Unit = macro log
def log(c: blackbox.Context)(message: c.Expr[String], arguments: c.Expr[Any]*): c.Expr[Unit] = {
import c.universe._
def exprsToExpr[A: WeakTypeTag](exprs: Seq[Expr[A]]): Expr[Seq[A]] =
exprs.foldRight(reify { Seq.empty[A] }) { (expr, acc) => reify {
expr.splice +: acc.splice
}}
reify {
println(message.splice)
for (argument <- exprsToExpr(arguments).splice) {
println(argument)
}
}
}
}
Macro.logMacro("a", 1, 2, 3)
// scalacOptions += "-Ymacro-debug-lite"
//{
// Predef.println("a");
// {
// final <synthetic> <artifact> val rassoc$1 = 1;
// {
// final <synthetic> <artifact> val rassoc$1 = 2;
// {
// final <synthetic> <artifact> val rassoc$1 = 3;
// `package`.Seq.empty[Any].$plus$colon(rassoc$1)
//}.$plus$colon(rassoc$1)
//}.$plus$colon(rassoc$1)
//}.foreach(((argument) => Predef.println(argument)))
//}
//a
//1
//2
//3
The tree is slightly different but runtime result is the same.
You couldn't do argument.splice because argument is not an Expr, it's a local variable defined here, in a scope quoted with reify. Trying to splice it is "cross-stage evaluation", you were trying to splice not a value from the current stage (Expr[A]) but from the next stage (A).
For the same reason .eval (calculating an Expr[A] into A) didn't work.
Difference between .splice and .eval is that the former just inserts a tree (into a tree) while the latter calculates it (if possible).

Related

How to write generic function with Scala Quill.io library

I am trying to implement generic method in Scala operating on database using Quill.io library. Type T will be only case classes what works with Quill.io.
def insertOrUpdate[T](inserting: T, equality: (T,T) => Boolean)(implicit ctx: Db.Context): Unit = {
import ctx._
val existingQuery = quote {
query[T].filter { dbElement: T =>
equality(dbElement, inserting)
}
}
val updateQuery = quote {
query[T].filter { dbElement =>
equality(dbElement, lift(inserting))
}.update(lift(inserting))
}
val insertQuery = quote { query[T].insert(lift(inserting)) }
val existing = ctx.run(existingQuery)
existing.size match {
case 1 => ctx.run(updateQuery)
case _ => ctx.run(insertQuery)
}
}
But I am getting two types of compile error
Error:(119, 12) Can't find an implicit `SchemaMeta` for type `T`
query[T].filter { dbElement: T =>
Error:(125, 33) Can't find Encoder for type 'T'
equality(dbElement, lift(inserting))
How can I modify my code to let it work?
As I said in the issue that #VojtechLetal mentioned in his answer you have to use macros.
I added code implementing generic insert or update in my example Quill project.
It defines trait Queries that's mixed into context:
trait Queries {
this: JdbcContext[_, _] =>
def insertOrUpdate[T](entity: T, filter: (T) => Boolean): Unit = macro InsertOrUpdateMacro.insertOrUpdate[T]
}
This trait uses macro that's implementing your code with minor changes:
import scala.reflect.macros.whitebox.{Context => MacroContext}
class InsertOrUpdateMacro(val c: MacroContext) {
import c.universe._
def insertOrUpdate[T](entity: Tree, filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
val updateQuery = ${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}
val insertQuery = quote {
query[$t].insert(lift($entity))
}
run(${c.prefix}.query[$t].filter($filter)).size match {
case 1 => run(updateQuery)
case _ => run(insertQuery)
}
()
"""
}
Usage examples:
import io.getquill.{PostgresJdbcContext, SnakeCase}
package object genericInsertOrUpdate {
val ctx = new PostgresJdbcContext[SnakeCase]("jdbc.postgres") with Queries
def example1(): Unit = {
val inserting = Person(1, "")
ctx.insertOrUpdate(inserting, (p: Person) => p.name == "")
}
def example2(): Unit = {
import ctx._
val inserting = Person(1, "")
ctx.insertOrUpdate(inserting, (p: Person) => p.name == lift(inserting.name))
}
}
P.S. Because update() returns number of updated records your code can be simplified to:
class InsertOrUpdateMacro(val c: MacroContext) {
import c.universe._
def insertOrUpdate[T](entity: Tree, filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
if (run(${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}) == 0) {
run(quote {
query[$t].insert(lift($entity))
})
}
()
"""
}
As one of the quill contributors said in this issue:
If you want to make your solution generic then you have to use macros because Quill generates queries at compile time and T type has to be resolved at that time.
TL;DR The following did not work either, just playing
Anyway... just out of curiosity I tried to fix the issue by following the error which you mentioned. I changed the definition of the function as:
def insertOrUpdate[T: ctx.Encoder : ctx.SchemaMeta](...)
which yielded the following log
[info] PopulateAnomalyResultsTable.scala:71: Dynamic query
[info] case _ => ctx.run(insertQuery)
[info]
[error] PopulateAnomalyResultsTable.scala:68: exception during macro expansion:
[error] scala.reflect.macros.TypecheckException: Found the embedded 'T', but it is not a case class
[error] at scala.reflect.macros.contexts.Typers$$anonfun$typecheck$2$$anonfun$apply$1.apply(Typers.scala:34)
[error] at scala.reflect.macros.contexts.Typers$$anonfun$typecheck$2$$anonfun$apply$1.apply(Typers.scala:28)
It starts promising, since quill apparently gave up on static compilation and made the query dynamic. I checked the source code of the failing macro and it seems that quill is trying to get a constructor for T which is not known in the current context.
For more details see my answer Generic macro with quill or implementation
CrudMacro:
Complete project you will find on quill-generic
package pl.jozwik.quillgeneric.quillmacro
import scala.reflect.macros.whitebox.{ Context => MacroContext }
class CrudMacro(val c: MacroContext) extends AbstractCrudMacro {
import c.universe._
def callFilterOnIdTree[K: c.WeakTypeTag](id: Tree)(dSchema: c.Expr[_]): Tree =
callFilterOnId[K](c.Expr[K](q"$id"))(dSchema)
protected def callFilterOnId[K: c.WeakTypeTag](id: c.Expr[K])(dSchema: c.Expr[_]): Tree = {
val t = weakTypeOf[K]
t.baseClasses.find(c => compositeSet.contains(c.asClass.fullName)) match {
case None =>
q"$dSchema.filter(_.id == lift($id))"
case Some(base) =>
val query = q"$dSchema.filter(_.id.fk1 == lift($id.fk1)).filter(_.id.fk2 == lift($id.fk2))"
base.fullName match {
case `compositeKey4Name` =>
q"$query.filter(_.id.fk3 == lift($id.fk3)).filter(_.id.fk4 == lift($id.fk4))"
case `compositeKey3Name` =>
q"$query.filter(_.id.fk3 == lift($id.fk3))"
case `compositeKey2Name` =>
query
case x =>
c.abort(NoPosition, s"$x not supported")
}
}
}
def createAndGenerateIdOrUpdate[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
val filter = callFilter[K, T](entity)(dSchema)
q"""
import ${c.prefix}._
val id = $entity.id
val q = $filter
val result = run(
q.updateValue($entity)
)
if (result == 0) {
run($dSchema.insertValue($entity).returningGenerated(_.id))
} else {
id
}
"""
}
def createWithGenerateIdOrUpdateAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
val filter = callFilter[K, T](entity)(dSchema)
q"""
import ${c.prefix}._
val id = $entity.id
val q = $filter
val result = run(
q.updateValue($entity)
)
val newId =
if (result == 0) {
run($dSchema.insertValue($entity).returningGenerated(_.id))
} else {
id
}
run($dSchema.filter(_.id == lift(newId)))
.headOption
.getOrElse(throw new NoSuchElementException(s"$$newId"))
"""
}
}

Import implicit values from a companion object but not used

I wrote some code which acquires some implicit values in the companion object like this:
package example.implicits
class Test {
import Test.GetValue
import Test.Implicits._
val intV = getV[Int]
val stringV = getV[String]
private def getV[T](implicit getV: GetValue[T]): T = getV.value
}
object Test {
trait GetValue[T] {
def value: T
}
object Implicits {
implicit val intValue = new GetValue[Int] {
def value = 10
}
implicit val stringValue = new GetValue[String] {
def value = "ten"
}
}
}
This piece of code cannot be compiled and the compiler complains it couldn't find the required implicit values. Note that my environment is
scala 2.11.8 on Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66
However, if I use these values explicitly, nothing goes wrong:
class Test {
import Test.GetValue
import Test.Implicits._
val intV = getV[Int](intValue)
val stringV = getV[String](stringValue)
private def getV[T](implicit getV: GetValue[T]): T = getV.value
}
Further more, if I declare new implicit values as following:
class Test {
import Test.GetValue
import Test.Implicits._
implicit val intValue1 = intValue
implicit val stringValue1 = stringValue
val intV = getV[Int]
val stringV = getV[String]
private def getV[T](implicit getV: GetValue[T]): T = getV.value
}
errors will be raised because of ambiguous implicit values.
When I swap position of class Test and object Test, everything goes right:
object Test {
trait GetValue[T] {
def value: T
}
object Implicits {
implicit val intValue = new GetValue[Int] {
def value = 10
}
implicit val stringValue = new GetValue[String] {
def value = "ten"
}
}
}
class Test {
import Test.GetValue
import Test.Implicits._
val intV = getV[Int]
val stringV = getV[String]
private def getV[T](implicit getV: GetValue[T]): T = getV.value
}
So why can't scala find implicit values after I've already imported them in the first case?
And why it can do so when I swap their position?
That's because the compiler hasn't infered the type of Test.intValue yet at the time when it resolves the implicits in getV[Int].
Just give Test.intValue and Test.stringValue their type explicitly, and your problem will be solved.
I read somewhere (sorry, can't remember where exactly), that implicit definitions should always be given an explicit type, notably to avoid this kind of behaviour.

Determine if type is Option[_] in scala compiler plugin

How can I determine whether a Tree object is a subtype of Option[_] in a scalac (2.11) plugin?
Example:
class OptionIfPhase(prev: Phase) extends StdPhase(prev) {
override def name = OptionIf.this.name
override def apply(unit: CompilationUnit) {
for (tree # Apply(TypeApply(Select(rcvr, TermName("map")), typetree), List(param)) <- unit.body;
if rcvr.tpe <:< definitions.OptionClass.tpe)
{
reporter.echo(tree.pos, s"found a map on ${rcvr} with type ${rcvr.tpe} and typelist ${typetree} and arg $param")
}
}
This if-guard is always false, unfortunately. If I remove it completely, it matches all "typed" maps (as expected). Here's a few examples of expressions that I would like to match:
// All of these maps
val x = Some(4).map(_ * 2)
val xo: Option[Int] = x
xo.map(_ * 3)
None.map(i => "foo")
// Not this one
List(1, 3, 5).map(_ + 1) // removing the if guard completely also matches this
// The inner one
List(Some(1), None, Some(3)).flatMap(_.map(_ * -1))
// And from methods:
def foo: Option[Int] = Some(3)
foo.map(_ + 1)
I tried this:
if rcvr.tpe <:< typeOf[Option[_]]
Which works in the REPL (after import scala.reflect.runtime.universe._), but in the plugin it throws an exception: "scala.ScalaReflectionException: class net.obrg.optionif.OptionIf in compiler mirror not found."
What is an appropriate if condition in the for loop to filter out any "map" on an expression that has been determined to be of type Option[_]?
Background: I'm trying to build a scala plugin that converts all x.map(f) expressions, where x is of type Option, to (if (x.isEmpty) None else Some(f(x.get))).
The surrounding plugin boilerplate, so far:
package net.obrg.optionif
import scala.tools.nsc.{Global, Phase}
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
class OptionIf(val global: Global) extends Plugin {
import global._
override val name = "optionif"
override val description = "flattens map operations on options to if statements"
override val components = List[PluginComponent](Component)
private object Component extends PluginComponent {
override val global: OptionIf.this.global.type = OptionIf.this.global
// I'm not 100% on these constraints, but afaik these are appropriate outer bounds.
// The typer phase is, obviously, necessary to determine which objects are of type Option, to begin with.
override val runsAfter = List("typer")
// The refchecks phase seems to do some if-related optimizations ("Eliminate branches in a conditional if the
// condition is a constant"). So put it before this, at least.
override val runsBefore = List("refchecks")
override val phaseName = OptionIf.this.name
override def newPhase(_prev: Phase) = new OptionIfPhase(_prev)
class OptionIfPhase(prev: Phase) extends StdPhase(prev) {
override def name = OptionIf.this.name
override def apply(unit: CompilationUnit) {
for (tree # Apply(TypeApply(Select(rcvr, TermName("map")), typetree), List(param)) <- unit.body;
if rcvr.tpe <:< definitions.OptionClass.tpe)
{
reporter.echo(tree.pos, s"found a map on ${rcvr} with type ${rcvr.tpe} and typelist ${typetree} and arg $param")
}
}
}
}
}
Adapted from http://www.scala-lang.org/old/node/140
EDIT: I just added the test for None.map(x => "foo") and the if guard actually does catch this! Still not the other ones, though.

Scala Reflection Conundrum: Can you explain these weird results?

I wrote some Scala code, using reflection, that returns all vals in an object that are of a certain type. Below are three versions of this code. One of them works but is ugly. Two attempts to improve it don't work, in very different ways. Can you explain why?
First, the code:
import scala.reflect.runtime._
import scala.util.Try
trait ScopeBase[T] {
// this version tries to generalize the type. The only difference
// from the working version is [T] instead of [String]
def enumerateBase[S: universe.TypeTag]: Seq[T] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[S].decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[T])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
}
trait ScopeString extends ScopeBase[String] {
// This version works but requires passing the val type
// (String, in this example) explicitly. I don't want to
// duplicate the code for different val types.
def enumerate[S: universe.TypeTag]: Seq[String] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[S].decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[String])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
// This version tries to avoid passing the object's type
// as the [S] type parameter. After all, the method is called
// on the object itself; so why pass the type?
def enumerateThis: Seq[String] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[this.type].decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[String])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
}
// The working example
object Test1 extends ScopeString {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerate[Test1.type]
}
// This shows how the attempt to generalize the type doesn't work
object Test2 extends ScopeString {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerateBase[Test2.type]
}
// This shows how the attempt to drop the object's type doesn't work
object Test3 extends ScopeString {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerateThis
}
val test1 = Test1.fields // List(test)
val test2 = Test2.fields // List(13, test)
val test3 = Test3.fields // List()
The "enumerate" method does work. However, as you can see from the Test1 example, it requires passing the object's own type (Test1.type) as a parameter, which should not have been necessary. The "enumerateThis" method tries to avoid that but fails, producing an empty list. The "enumerateBase" method attempts to generalize the "enumerate" code by passing the val type as a parameter. But it fails, too, producing the list of all vals, not just those of a certain type.
Any idea what's going on?
Your problem in your generic implementation is the loss of the type information of T. Also, don't use exceptions as your primary method of control logic (it's very slow!). Here's a working version of your base.
abstract class ScopeBase[T : universe.TypeTag, S <: ScopeBase[T, S] : universe.TypeTag : scala.reflect.ClassTag] {
self: S =>
def enumerateBase: Seq[T] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[S].baseClasses.map(_.asType.toType).flatMap(
_.decls
.filter(_.typeSignature.resultType <:< universe.typeOf[T])
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.map(decl => mirror.reflectMethod(decl).apply().asInstanceOf[T])
.filter(_ != null)
).toSeq
}
}
trait Inherit {
val StringField2: String = "test2"
}
class Test1 extends ScopeBase[String, Test1] with Inherit {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerateBase
}
object Test extends App {
println(new Test1().fields)
}
Instead of getting the type from universe.typeOf you can use the runtime class currentMirror.classSymbol(getClass).toType, below is an example that works:
def enumerateThis: Seq[String] = {
val mirror = currentMirror.reflect(this)
currentMirror.classSymbol(getClass).toType.decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[String])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
//prints List(test)
With everyone's help, here's the final version that works:
import scala.reflect.runtime.{currentMirror, universe}
abstract class ScopeBase[T: universe.TypeTag] {
lazy val enumerate: Seq[T] = {
val mirror = currentMirror.reflect(this)
currentMirror.classSymbol(getClass).baseClasses.map(_.asType.toType).flatMap {
_.decls
.filter(_.typeSignature.resultType <:< universe.typeOf[T])
.filter(_.isMethod)
.map(_.asMethod)
.filterNot(_.isConstructor)
.filter(_.paramLists.size == 0)
.map(decl => mirror.reflectField(decl.asMethod).get.asInstanceOf[T])
.filter(_ != null).toSeq
}
}
}
trait FieldScope extends ScopeBase[Field[_]]
trait DbFieldScope extends ScopeBase[DbField[_, _]] {
// etc....
}
As you see from the last few lines, my use cases are limited to scope objects for specific field types. This is why I want to parameterize the scope container. If I wanted to enumerate the fields of multiple types in a single scope container, then I would have parameterized the enumerate method.

Scala macro for shortcut

I have defined the following macros to get file, line and object/class from current location:
http://pastebin.com/UsNLemnK
Using SBT, I have defined two projects, in order to compile the macros first, then the actual project using these macros.
The purpose of these macros are to be be used in a log method:
def log( msg: Any, srcFile: String = "", srcLine: String = "", srcClass:String = "")
I am then using this log method as follows:
log(msg, s"$F_",s"$L_",s"$C_")
where F_, L_ and C_ are defined in the macro.
Now, I would like to create a shortcut to avoid this boilerplate and just call:
log(msg)
which should automatically be replaced by
log(msg, s"$F_",s"$L_",s"$C_")
I could define a macro to do this:
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_") )
}
but again, this macro needs to be compiled before the project, where the log function itself is defined... So I don't see how to solve the compilation dependencies cycle...
Any suggestion about how to do this?
Thanks
Barring the use of macro annotations (which would necessarily and significantly alter your API's syntax), the problem you have to face is that you need the type-checked identifier of your log function.
Since you can't import the entire log implementation, a solution would be to:
wrap the method into a trait,
define this trait in the "macro" project,
add an implicit parameter to the log_ method,
in your "main" project, create an implementation of this trait, and instantiate this implementation in an implicit val visible everywhere you'd like to use the log_ macro (in the package object for example).
Of course, you could also use a simple FunctionN here and avoid the trait definition and implementation, but this way you'll avoid potential conflicts with other same-typed implicits.
In general, your code would resemble the following:
//"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(...)
In your specific case, here's how an implementation could look like:
//"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")
}
}
(note that I had to correct the signature of log_ and tweak the macro call a bit)