【问题标题】:c++ static member in scala generic programmingscala泛型编程中的c ++静态成员
【发布时间】:2017-07-23 17:39:39
【问题描述】:

我曾经是一名 c++ 程序员,并且是 scala 的新手。 而我在 scala 中如何做一些通用编程,例如

   class FooComponent
    {
     public:
       static const int ComponentId = 1;
    }
    class BarComponent
    {
     public:
       static const int ComponentId = 2;
    }
    template<typename T>
    void registerComponent()
    {  
       register(T::ComponentId)
    }

但在 scala 中没有类静态变量。我知道有伴生对象,但我不能仅通过类型参数访问伴生对象。

在 scala 中实现类似功能的正确方法是什么?

【问题讨论】:

  • 不是c++程序员,不是说FooComponent继承了模板吗??还有当你做T::ComponentId时,T甚至没有ComponentId?? 你可以忽略我的问题,我不知道 c++ 模板:)
  • @prayagupd - registerComponent&lt;BarComponent&gt;() 类型的调用将使用主体 register(BarComponent::ComponentId) 实例化一个函数。 T 是此处类型的替代,而不是任何特定类型。
  • @prayagupd 你说得对。它不知道 T 是否有 ComponentId。但是当你尝试实例化一个类型为 T 且没有 ComponentId 的函数时,它会抛出编译错误
  • 这看起来很有趣,所以你可以调用其中没有ComponentId 定义的template,它会编译。我的猜测是模板更接近 scala/java/OO 中的接口,您可以在其中预定义实现类中的内容。
  • 我的尝试,不完全是c++模板拷贝,更接近scastie.scala-lang.org/prayagupd/s5CPeKhsTs2hkg96fwDGKg/1

标签: scala generics


【解决方案1】:

您可以使用结构类型来接近这一点。但据我所知,您不能将其与伴随对象的静态成员结合使用。

class FooComponent{
  val ComponentId = 1
}

class BarComponent{
  val ComponentId = 2
}

object Main extends App{
  def register(t: {val ComponentId:Int} ) = println(t.ComponentId)

  register(new BarComponent())  // -> 2
  register(new FooComponent())  // -> 1
}

如果您需要同时访问实例和类对象,您可以这样做:

class FooComponent{
  val ComponentId = FooComponent.ComponentId
}
object FooComponent{
  val ComponentId = 1
}

class BarComponent{
  val ComponentId = BarComponent.ComponentId
}
object BarComponent{
  val ComponentId = 2
}

object Main extends App{
  def register(t: {val ComponentId:Int} ) = println(t.ComponentId)

  register(BarComponent)      // -> 2
  register(new BarComponent)  // -> 2
  register(FooComponent)      // -> 1
  register(new FooComponent)  // -> 1

  println(FooComponent.ComponentId)   // -> 1
}

【讨论】:

  • 我可以只提取 id 而不更新对象吗?
  • 对于静态,您需要一个同名的伴随对象,例如对象栏组件。如果你把你的 val 放在那里,它可以在不创建实例的情况下访问。
  • 我了解您的解决方案,它将解决问题,但我想知道是否有更好的方法。因为在你的解决方案中,你必须传入一个对象,并且在runtime获取这个对象中的componentId,而在c++示例中,不需要传入一个对象和所有componentId将在编译时确定
  • 我不确定您是否可以在 Scala 中完全重建您的示例。也许有一种(更复杂的)我看不到的方法来做到这一点。
  • 如果您不需要使用实例调用 register() ,则不需要用于此的类。我不确定您真正想要归档什么,但也许有一种更好的类似 Scala 的方法可以在不使用静态组件 ID 的情况下执行相同的操作。例如您可以直接使用类型或伴侣。
【解决方案2】:

如果您真的只想使用类型参数(并且更像模板),您可以使用宏来实现:

在宏项目中创建实际的宏

import scala.reflect.macros.blackbox

object MyMacroImpl {
 def registerMacro[T: c.WeakTypeTag](c: blackbox.Context): c.universe.Tree = {
  import c.universe._

  val tpe = weakTypeOf[T]
  val companion = tpe.typeSymbol.companion

  q"""MyMacro.register(${companion.name.toTermName}.componentId)"""

}

在你的项目中使用宏

class FooComponent
object FooComponent {
  val componentId: Int = 1
}

class BarComponent
object BarComponent {
  val componentId: Int = 2
}

object MyMacro {
  def registerComponent[T]: Unit = macro MyMacroImpl.registerMacro[T]
}

object Main {
  def register(id: Int): Unit = println(id)

  MyMacro.registerComponent[FooComponent]
}

编辑:就个人而言,我会像这样实现它:

def register(id: Int): Unit = println(id)

trait HasComponentId {
  def componentId: Int
}

class FooComponent
object FooComponent extends HasComponentId {
  override val componentId: Int = 1
}

class BarComponent
object BarComponent extends HasComponentId {
  override val componentId: Int = 2
}

def registerComponent(comp: HasComponentId): Unit =
  register(comp.componentId)


assert(registerComponent(FooComponent) == 1)

没有宏(因此复杂性要低得多),没有对象的实例化,结果几乎相同。

【讨论】:

  • 我不熟悉 scala 中的宏,但代码看起来像我什么。感谢您的回答,我会尝试宏观方式。
猜你喜欢
  • 1970-01-01
  • 2012-04-17
  • 2010-09-06
  • 1970-01-01
  • 2016-06-16
  • 2013-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多