可以将类型实例化与值实例化分开吗?

时间:2018-04-19 21:50:29

标签: chapel

在C ++中,我可以在编译时实例化泛型类型,然后在运行时构造它:

struct Vertex {};
struct Edge   {};

template<typename NodeType, typename IdType>
struct Wrapper {
  IdType id;

  Wrapper(IdType id) : id{id} {};
};

int main() {
  using vertexWrapper = Wrapper<Vertex, int>;
  vertexWrapper v(3);
}

变量清楚地分开,类型看起来/感觉不像值。我想在Chapel做类似的事情:

record Vertex {}
record Edge   {}

record Wrapper {
  type nodeType;
  type idType;   
  var id: idType;

  proc init(id) {
    this.id = id;
  }
}

type vertexWrapper = Wrapper(Vertex, int);

var v1 = 3: vertexWrapper;

当我编译这段代码时,我得到:

chpl -o graph-add-inclusion --cc-warnings test.chpl
test.chpl:9: In initializer:
test.chpl:10: error: can't omit initialization of field "nodeType", no type or default value provided
test.chpl:10: error: can't omit initialization of field "idType", no type or default value provided

有没有办法将类型构造与Chapel中的值构造分开,以实现标记类型的效果,因为我试图进入我的示例?我使用标记类型来实现两种实体(这里是顶点和边)常见的单一实现,但我希望这些实体是不同的类型。

还有一个相关的问题。我应该只写:

type vertexWrapper = Wrapper(Vertex);

然后从我的构造函数中分别推导出整数?

似乎在定义时检查构造函数,但没有可能与值分开提供类型。我是否做到了这一点,如果我做到了,这件事情将来会改变吗?

2 个答案:

答案 0 :(得分:4)

您遇到的问题是您定义的初始值设定项,而不是您使用的类型别名。由于你如何定义它,用户可以想象写这个:

var x = new Wrapper(1); // type is inferred from the new's return

并且初始化器不知道如何处理nodeType和idType字段。因此,您的初始化程序需要在其正文中明确设置它们。

我同意初始化程序在使用实例化时弄清楚它们的值会很好,并且预计这将是我们将来支持的内容。在短期内,你可以通过一些重复的工作获得你想要的东西。

要做到这一点,首先你可以更新初始化程序,以便它可以根据相应的参数找出idType。

proc init(id) {
  idType = id.type;
  this.id = id;
}

但是,这对nodeType字段没有帮助,因为初始化程序在您所描述的内容中没有相应的值字段。这意味着您必须手动将其提供给初始化程序。您可以通过访问其nodeType字段以一般方式从类型别名执行此操作:

var v1 = new vertexWrapper(vertexWrapper.nodeType, 3);

但是您还需要更新初始化程序以将其作为参数。因此,创建实例v1必须看起来像这样:

record Vertex {}
record Edge   {}

record Wrapper {
  type nodeType;
  type idType;   
  var id: idType;

  proc init(type nodeType, id) {
    this.nodeType = nodeType;
    this.idType = id.type;
    this.id = id;
  }
}

type vertexWrapper = Wrapper(Vertex, int);

var v1 = new vertexWrapper(vertexWrapper.nodeType, 3);

希望这是一个适用于您的用例的解决方案。

  

我应该只写:

type vertexWrapper = Wrapper(Vertex);
     

然后从我的构造函数中分别推导出整数?

我们目前不支持部分实例化,因此Wrapper(Vertex)不是您可以编写的内容。如果要将idType设置为像int这样的公共值,可以将其作为类型字段的默认值提供:

type idType = int;

允许您只通过Wrapper(Vertex)指定nodeType,但这意味着idType被锁定为int,除非您使用new(var x = new Wrapper(Vertex, 3.0);)创建实例化,或者除非您在其中指定其他内容与nodeType同时发生。

答案 1 :(得分:1)

作为这个答案的序言(正如我对这个问题的评论中所指出的),我认为Chapel的初始化器故事应该改进,以支持初始化器调用代表实例化泛型的类型别名,就像你想要做的那样。

在此之前,我尝试的这个想法似乎有效 [Try It Online]。基本上,我正在使用类型方法 -ie在newNode()类型本身上创建一个工厂方法Wrapper,一个在类型本身而不是在类型上调用的方法该类型的值:

record Vertex {}
record Edge   {}

record Wrapper {
  type nodeType;
  type idType;
  var id: idType;

  proc type newNode(id) {
    return new Wrapper(nodeType, idType, id);
  }
}

type vertexWrapper = Wrapper(Vertex, int);

var v1 = vertexWrapper.newNode(3);
writeln(v1);

由于vertexWrapper类型别名已定义nodeType,我可以在工厂方法中引用它,以便创建Wrapper的新实例。

我认为这可以被视为封装Lydia之前提出的解决方案的另一种方式。