Skip to content

Instantly share code, notes, and snippets.

@Arneball Arneball/Macro
Last active Aug 29, 2015

Embed
What would you like to do?
"Serialize" an sequence of objects to an sequence of AnyRefs containing the elements of the object
case class PackedArray[T](elems: Array[AnyRef])
object Packer {
def unapply[T](pa: PackedArray[T]): Seq[T] = macro unpack_impl[T]
def unpack_impl[T : c.WeakTypeTag](c: Context)(pa: c.Expr[PackedArray[T]]): c.Expr[Seq[T]] = {
import c.universe._
val tTyp = weakTypeOf[T]
val Some(fields) = tTyp.declarations.collectFirst {
case cons: MethodSymbol if cons.isPrimaryConstructor && !cons.isPublic =>
c.error(c.enclosingPosition, "No public primary constructor found")
Nil
case cons: MethodSymbol if cons.isPrimaryConstructor =>
cons.paramss.map{ syms => syms.map{ sym =>
q"${sym.name}" -> sym.typeSignature
}}
}
val realFields = fields.head
val fieldSize = realFields.size
val must = realFields.zipWithIndex.map{ case ((tree, sym), index) =>
q"$tree = group($index).asInstanceOf[$sym]"
}
val scalaSym = typeOf[Any].typeSymbol.owner
tTyp.baseClasses.collectFirst {
case sym if sym.name.decoded.startsWith("Tuple") && sym.owner == scalaSym =>
c.abort(c.enclosingPosition, "Not needed for tuples!")
}.getOrElse{
c.Expr[Seq[T]]{
q""" $pa.elems.grouped($fieldSize).map{ group =>
new $tTyp(..$must)
}.toList
"""
}
}
}
def pack[T](seq: Seq[T]): PackedArray[T] = macro pack_impl[T]
def pack_impl[T : c.WeakTypeTag](c: Context)(seq: c.Expr[Seq[T]]): c.Expr[PackedArray[T]] = {
import c.universe._
val ttyp = weakTypeOf[T]
val fields = ttyp.declarations.collect {
case sym: MethodSymbol if sym.isCaseAccessor => q"a.${sym.name}.asInstanceOf[AnyRef]"
}
val fieldLen = fields.size
val fieldFun = fields.zipWithIndex.map{ case (select, i) =>
q"array(index * $fieldLen + $i) = $select"
}
val dasBlock = Block(fieldFun.toList:_*)
if (fields.isEmpty) {
c.abort(c.enclosingPosition, "Not a case class!")
} else {
val scalaSym = typeOf[Any].typeSymbol.owner
weakTypeOf[T].baseClasses.collectFirst {
case sym
if sym.name.decoded.startsWith("Tuple") && sym.owner == scalaSym =>
c.abort(c.enclosingPosition, "Not needed for tuples!")
} getOrElse {
c.Expr[PackedArray[T]]{
q"""
val totalSize = $seq.size * $fieldLen
val array = new Array[AnyRef](totalSize)
$seq.zipWithIndex.foreach{ case (a, index) =>
$dasBlock
}
new PackedArray[$ttyp](array)
"""
}
}
}
}
}
case class Person(age: Int, name: String)
val packed: PackedArray[Person] = Packer.pack(List(Person(3, "Kalle"), Person(15, "Bertil")))
val elems: Array[AnyRef] = packed.elems
println{
elems mkString "," // prints 3,Kalle,15,Bertil
}
val seq: Seq[Person] = Packer.unapply(packed)
println(seq) // prints List(Person(3,Kalle), Person(15,Bertil))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.