2016-01-27 2 views
2

Я использую Play Framework и пытаюсь написать действие, которое может анализировать Protobuf запроса следующим образом:Quasiquotes и Дженерик в Scala

def AsyncProtoAction(block: ProtoRequestA => Future[Result]): Action[AnyContent] = { 
    Action.async { request => 
     request.contentType match { 
      case Some("application/protobuf") => request.body.asRaw match { 
       case Some(raw) => raw.asBytes(1024 * 1024) match { 
        case Some(rawBytes) => 
         val proto = ProtoRequestA.parseFrom(rawBytes) 
         block(proto) 
        case _ => ... 
       } 
      } 
     } 
    } 
} 

Здесь ProtoRequestA является сгенерированным объектом с помощью ScalaPB. Он работает, однако, у меня много объектов запроса protobuf. Затем я пытаюсь переписать его с помощью макроса:

object Actions { 
    def AsyncProtoAction[T](block: T => Future[Result]): 
     Action[AnyContent] = macro asyncProtoAction_impl[T] 

    def asyncProtoAction_impl[T: c.WeakTypeTag](c: blackbox.Context) 
     (block: c.Tree) = { import c.universe._ 
    val protoType = weakTypeOf[T] 
    q"""import play.api.mvc._ 
     Action.async { request => 
      request.contentType match { 
      case Some("application/protobuf") => 
       request.body.asRaw match { 
       case Some(raw) => 
        raw.asBytes(1024 * 1024) match { 
        case Some(rawBytes) => 
         val proto = $protoType.parseFrom(rawBytes) 
         $block(proto) 
        case _ => ... 
        } 
       } 
      } 
     }""" 
    } 
} 

Это не работает. Ошибка компиляции - value parseFrom is not a member of packageName.ProtoRequestA.

Я пробовал showRaw(tq"$protoType") в REPL, выход которого String = TypeTree(). Я думаю, правильный результат должен быть String = Select(Ident(TermName("packageName")), TypeName("ProtoRequestA")). И что же мне делать?

+0

Попробуйте заменить '$ protoType'' $ {protoType.typeSymbol.companionSymbol} 'и посмотреть, как это происходит. –

ответ

2

Вы идентифицировали проблему, которая заключается в том, что при интерполяции типа ProtoRequestA вы получаете что-то отличное от сопутствующего объекта ProtoRequestA. Одним из простых решений является интерполяция символа сопутствующего объекта, который вы можете получить от символа типа. Вот упрощенный пример:

import scala.language.experimental.macros 
import scala.reflect.macros.blackbox.Context 

def companionObjectBarImpl[A: c.WeakTypeTag](c: Context): c.Tree = { 
    import c.universe._ 
    q"${ symbolOf[A].companion }.bar" 
} 

def companionObjectBar[A]: Int = macro companionObjectBarImpl[A] 

Этот макрос будет работать на любом типе с объектом компаньона с bar метод, который возвращает Int. Например:

scala> class Foo; object Foo { def bar = 10 } 
defined class Foo 
defined object Foo 

scala> companionObjectBar[Foo] 
res0: Int = 10 

Вы должны быть в состоянии сделать что-то подобное.

Смежные вопросы