2015-10-22 2 views
2

Есть ли способ получить родительский класс из экземпляра внутреннего класса с использованием макросов, а не run-time reflection?Найти внешний класс, если предоставлен экземпляр внутреннего класса

У меня есть набор классов, как это:

trait IdProvider { 
    type IdObject = Id.type 
    case class Id(underlying: Int) 
} 

case class SomeEntity(id: SomeEntity.Id) 

object SomeEntity extends IdProvider 

И некоторый код, который работает с произвольными IdProvider#Id с:

val lookup = Map[IdProvider#IdObject, Set[Operation]] 

def can(operation: Operation, id: IdProvider#Id): Boolean = { 
    val idObject = findIdTypeFromInstance(id) // This is what I don't have 
    lookup.get(idObject).exists(s => s(operation)) 
} 

Изъятие лист из this gist by Paul P. теперь у меня есть этот макрос:

def findIdTypeFromInstance[T <: AnyRef : c.WeakTypeTag](
    c: blackbox.Context)(thing: c.Expr[T]): c.Expr[T] = { 
    import c.universe._ 
    val companion = thing.actualType.typeSymbol.companion match { 
    case NoSymbol => 
     c.abort(c.enclosingPosition, s"Instance of ${thing.actualType} has no companion object") 
    case sym => sym 
    } 

    def make[U: c.WeakTypeTag] = c.Expr[U](internal.gen.mkAttributedRef(companion)) 

    make(c.WeakTypeTag(companion.typeSignature)) 
} 

Это работает для более простых случаев (корпус верхнего уровня cla sses, classes и objects, и даже вложенные классы case). Однако при работе с IdProvider установки над макро пытается генерировать это дерево:

Select(This(TypeName("IdProvider")), TermName("Id")) 

Это приводит к очень длинной трассировки стека в моем тесте, который начинается с:

scala.reflect.internal.Types$TypeError: value is not a member of my.spec.MacroSpec

Я не имею был найден путь от экземпляра или компаньона (IdProvider#Id) к родительскому классу (в данном случае SomeEntity). Есть ли способ добраться до SomeEntity или мне нужно использовать run-time reflection?

ответ

1

Id компаньон в основном ленивый val. Вам нужно, чтобы экземпляр экземпляра извлекал его значение, потому что это не статически определенный стабильный путь.

С -Yshow-syms вы можете увидеть, что добавляются в mixin фазе:

 object SomeEntity 
      constructor SomeEntity 
*   method Id$lzycompute (private) 
      method apply (case <synthetic>) 
       value id 
      method readResolve (private <synthetic>) 
      method unapply (case <synthetic>) 
       value x$0 (<synthetic>) 
*   object Id (<synthetic> <stable>) 
      value <local SomeEntity> 
*   variable Id$module (private <local> <synthetic>) 

$outer поле из Id добавляется в explicitouter.

Легче ли просто разоблачить ссылку компаньона?

case class Id(underlying: Int) { 
    def c = Id 
    } 

Это просто быстрый взгляд; возможно, есть умный способ сделать это.

+0

Это хорошая идея. Я уже разоблачаю внешний экземпляр (с помощью типа 'self'), чтобы я мог получить таким образом. –

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