Reflection 模块中是否有类似“setField()”的例程?

问题描述 投票:0回答:1

我最近遇到了下面的 Reflection 模块(在标准库中):

https://chapel-lang.org/docs/modules/standard/Reflection.html

我想这个模块对于创建一个通用 I/O 库可能很有用,该库读取输入文件(例如 TOML),并通过扫描字段名称并将其与以下内容匹配来自动填充类/记录变量的所有字段输入文件。然而,Reflection 模块似乎只提供

getField()
,它将字段值作为右值返回。那么目前,是否无法执行类似
setField()
的操作来通过其名称或索引设置字段值?仅供参考,我感兴趣的是是否可以通过反射模块实现如下所示的功能:

对于 Swift:https://github.com/LebJe/TOMLKit

let toml = """
string = "Hello, World!"
int = 523053
double = 3250.34
"""

struct Test: Codable {
    let string: String
    let int: Int
    let double: Double
}
...
let test = Test(string: "Goodbye, World!", int: 24598, double: 18.247)

let encodedTest: TOMLTable = try TOMLEncoder().encode(test)
let decodedTest = try TOMLDecoder().decode(Test.self, from: encodedTest)

print("Encoded Test: \n\(encodedTest)\n")
print("Decoded Test: \(decodedTest)")

--> Result:

Encoded Test:
double = 18.247
int = 24598
string = 'Goodbye, World!'

Decoded Test: Test(string: "Goodbye, World!", int: 24598, double: 18.247)

对于 Rust 来说:https://docs.rs/toml/latest/toml/

类似的例子可能是 Fortran 中的“namelist”功能,它可以直接从输入文件中读入用户定义的类型变量,例如如下:

!! test.f90
program main
    implicit none
    type Foo
        integer :: num
        real :: val
    endtype

    type(Foo) :: f
    namelist /myinp/ f

    open( 10, file="test.inp", status="old" )
    read( 10, nml=myinp )
    close( 10 )

    print *, "f % num = ", f % num
    print *, "f % val = ", f % val
end

!! test.inp
&myinp
  f%num = 100
  f%val = 1.23
/

$ gfortran test.f90 && ./a.out
 f % num =          100
 f % val =    1.23000002  
reflection chapel
1个回答
0
投票

我稍后会回答您有关反射的问题,但我首先想让您注意 IO 模块提供的序列化器和反序列化器功能。

以下是基本概述:https://chapel-lang.org/docs/modules/standard/IO.html#the-serialize-and-deserialize-methods

这里有一个更深入文档的链接,适合编写您自己的[反]序列化器或向类型添加自定义[反]序列化:https://chapel-lang.org/docs/technotes/ioSerializers.html

这是使用 JSON mdodule 的示例

use IO, JSON;

record Point {
  var x : int;
  var y : int;
}

proc main() {
  var temp = openTempFile();
  var orig = new Point(5, 10);

  // Write 'orig' in JSON to our in-memory file
  {
    var wr = temp.writer(locking=false, serializer=new jsonSerializer());
    wr.write(orig);
  }

  // Read the temp file's raw data to prove it wrote JSON
  {
    const data = temp.reader(locking=false).readAll(string);
    writeln("Wrote: ", data);
  }

  // Create a 'fileReader' configured to read JSON, and read the Point back in
  var rd = temp.reader(locking=false, deserializer=new jsonDeserializer());
  var pt = rd.read(Point);
  writeln("Read: ", pt);
}

该程序打印:

Wrote: {"x":5, "y":10}
Read: (x = 5, y = 10)

Reflection 模块支持

getFieldRef
(请参阅 https://chapel-lang.org/docs/main/modules/standard/Reflection.html#Reflection.getFieldRef),它返回可变引用。这允许您为该字段分配值。另请注意,
getFieldRef
目前在 2.0 版本中不稳定(在发表此评论时是最新版本)。

以下是一个示例程序,演示了如何使用

getFieldRef
以编程方式读取泛型类型,以防您或其他人发现 [De]Serializers 不够。

use IO, Reflection;

// for 'isParam', 'isType'
use Types;

record A {
  var x : int;
  var y : string;
}

record B {
  var i : int;
  var j : int;
  var k : int;
}

record C {
  type T;
  var x : T;
}

proc readGeneric(reader : fileReader(?), ref arg: ?) {
  param numFields = getNumFields(arg.type);

  for param i in 0..<numFields {
    // 'type' and 'param' fields must be known at compilation time
    if !(isParam(getField(arg, i)) || isType(getField(arg, i))) {
      param name : string = getFieldName(arg.type, i);
      ref fieldRef = getFieldRef(arg, i);

      // %? = read according to type of 'fieldRef'
      reader.readf(name + ": %?\n", fieldRef);
    }
  }
}

proc test(type T, data: string) {
  var temp = openTempFile();
  {
    // In brackets to force temporary writer to flush at end of scope
    temp.writer(locking=false).write(data);
  }

  var r = temp.reader(locking=false);
  var val : T;
  readGeneric(r, val);
  writeln("Read type '" + T:string + "': ", val);
}

proc main() {
  const AData =
'''x: 5
y: "hello"''';
  test(A, AData);

  const BData =
'''i: 1
j: 2
k: 3''';
  test(B, BData);

  // Examples of generic types
  const CData_int = '''x: 5''';
  test(C(int), CData_int);

  const CData_string = '''x: "five"''';
  test(C(string), CData_string);
}

该程序打印:

Read type 'A': (x = 5, y = "hello")
Read type 'B': (i = 1, j = 2, k = 3)
Read type 'C(int(64))': (x = 5)
Read type 'C(string)': (x = "five")
© www.soinside.com 2019 - 2024. All rights reserved.