关联数组的编译时初始化
Compile time initialization of an associative array
根据 D 语言参考 static initialization of associative arrays 关联数组 (AA) 可以这样初始化:
immutable long[string] aa = [
"foo": 5,
"bar": 10,
"baz": 2000
];
void main()
{
import std.stdio : writefln;
writefln("(aa = %s)", aa);
}
但是该示例无法使用合理的最新 DMD 进行编译:
$ dmd --version
DMD64 D Compiler v2.083.0
Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved written by Walter Bright
$ dmd -de -w so_003.d
so_003.d(3): Error: non-constant expression ["foo":5L, "bar":10L, "baz":2000L]
谷歌搜索似乎表明这是语言中长期存在的错误(?):
- Cannot initialize associative array
- What is the syntax for declaring a constant string[char] AA?
所以我知道如何使用 static constructor 解决这个问题。但是考虑到这个问题已经存在了大约 10 年,这实际上变成了一个功能吗?
事实上,这只是我实际问题的前奏:
是否可以在编译时初始化关联数组?
在下面的示例中,我可以使用 运行 在编译时(使用 CTFE)生成器函数初始化模块级别 string[] doubleUnits
,pragma(msg)
证明了这一点.我可以在 运行 时间内初始化 int[string] doubleUnitMap
。但是我如何在编译时初始化 AA?
import std.stdio : writefln;
immutable char[] units = ['a', 'b', 'c'];
immutable string[] doubleUnits = generateDoubleUnits(units);
pragma(msg, "compile time: ", doubleUnits);
string[] generateDoubleUnits(immutable char[] units)
pure
{
import std.format : format;
string[] buffer;
foreach(unit; units) {
buffer ~= format("%s%s", unit, unit);
}
return buffer;
}
immutable int[string] doubleUnitMap;
// pragma(msg) below triggers the following compilation error:
// Error: static variable doubleUnitMap cannot be read at compile time
// while evaluating pragma(msg, "compile time: ", doubleUnitMap)
// pragma(msg, "compile time: ", doubleUnitMap);
shared static this() {
doubleUnitMap = generateDoubleUnitMap(units);
}
int[string] generateDoubleUnitMap(immutable char[] units)
pure
{
import std.format : format;
int[string] buffer;
foreach(unit; units) {
string key = format("%s%s", unit, unit);
buffer[key] = 1;
}
return buffer;
}
void main()
{
writefln("(doubleUnits = %s)", doubleUnits);
writefln("(doubleUnitMap = %s)", doubleUnitMap);
}
目前,这是不可能的(如语言规范文档中所述)。我已经提交了规范中的更改,并指出该功能尚未实现。肯定是有计划的,但还没有实施...
不可能执行在编译时初始化的内置 AA,因为编译器不知道运行时格式。它知道运行时接口,它知道编译时内存布局......但是运行时内存布局委托给库,所以编译器不知道如何形成它。因此错误。
但是,如果你要实现你自己的 AA 类型实现,那么你可以编写 CTFE 代码来布局它,然后编译器可以在编译时实现它。
很多年前,有人提议将其作为一种修复方法 - 用恰好适合编译器接口的库 AA 替换内置的魔法实现。然后它就可以做到这一切。问题是库类型无法表达内置关联数组的所有魔力。我不记得确切的问题,但我认为这是关于 const 和其他属性交互的问题。
但话虽如此,即使 100% 替换失败,您自己实施 90% 替换可能对您来说已经足够了。声明看起来会有所不同 - MyAA!(string, int)
而不是 string[int]
,并且它的文字也不同(尽管可能 makeMyAA(["foo" : 10])
;一个辅助 ctfe 函数,它采用内置文字并将其转换为您的格式),但由于运算符重载,用法基本相同。
当然,实现你自己的 AA 可能需要一些代码,也许不值得,但如果 CT 初始化是必须的,这是让它工作的方法。
(我个人觉得静态构造函数已经足够好了...)
根据 D 语言参考 static initialization of associative arrays 关联数组 (AA) 可以这样初始化:
immutable long[string] aa = [
"foo": 5,
"bar": 10,
"baz": 2000
];
void main()
{
import std.stdio : writefln;
writefln("(aa = %s)", aa);
}
但是该示例无法使用合理的最新 DMD 进行编译:
$ dmd --version
DMD64 D Compiler v2.083.0
Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved written by Walter Bright
$ dmd -de -w so_003.d
so_003.d(3): Error: non-constant expression ["foo":5L, "bar":10L, "baz":2000L]
谷歌搜索似乎表明这是语言中长期存在的错误(?):
- Cannot initialize associative array
- What is the syntax for declaring a constant string[char] AA?
所以我知道如何使用 static constructor 解决这个问题。但是考虑到这个问题已经存在了大约 10 年,这实际上变成了一个功能吗?
事实上,这只是我实际问题的前奏:
是否可以在编译时初始化关联数组?
在下面的示例中,我可以使用 运行 在编译时(使用 CTFE)生成器函数初始化模块级别 string[] doubleUnits
,pragma(msg)
证明了这一点.我可以在 运行 时间内初始化 int[string] doubleUnitMap
。但是我如何在编译时初始化 AA?
import std.stdio : writefln;
immutable char[] units = ['a', 'b', 'c'];
immutable string[] doubleUnits = generateDoubleUnits(units);
pragma(msg, "compile time: ", doubleUnits);
string[] generateDoubleUnits(immutable char[] units)
pure
{
import std.format : format;
string[] buffer;
foreach(unit; units) {
buffer ~= format("%s%s", unit, unit);
}
return buffer;
}
immutable int[string] doubleUnitMap;
// pragma(msg) below triggers the following compilation error:
// Error: static variable doubleUnitMap cannot be read at compile time
// while evaluating pragma(msg, "compile time: ", doubleUnitMap)
// pragma(msg, "compile time: ", doubleUnitMap);
shared static this() {
doubleUnitMap = generateDoubleUnitMap(units);
}
int[string] generateDoubleUnitMap(immutable char[] units)
pure
{
import std.format : format;
int[string] buffer;
foreach(unit; units) {
string key = format("%s%s", unit, unit);
buffer[key] = 1;
}
return buffer;
}
void main()
{
writefln("(doubleUnits = %s)", doubleUnits);
writefln("(doubleUnitMap = %s)", doubleUnitMap);
}
目前,这是不可能的(如语言规范文档中所述)。我已经提交了规范中的更改,并指出该功能尚未实现。肯定是有计划的,但还没有实施...
不可能执行在编译时初始化的内置 AA,因为编译器不知道运行时格式。它知道运行时接口,它知道编译时内存布局......但是运行时内存布局委托给库,所以编译器不知道如何形成它。因此错误。
但是,如果你要实现你自己的 AA 类型实现,那么你可以编写 CTFE 代码来布局它,然后编译器可以在编译时实现它。
很多年前,有人提议将其作为一种修复方法 - 用恰好适合编译器接口的库 AA 替换内置的魔法实现。然后它就可以做到这一切。问题是库类型无法表达内置关联数组的所有魔力。我不记得确切的问题,但我认为这是关于 const 和其他属性交互的问题。
但话虽如此,即使 100% 替换失败,您自己实施 90% 替换可能对您来说已经足够了。声明看起来会有所不同 - MyAA!(string, int)
而不是 string[int]
,并且它的文字也不同(尽管可能 makeMyAA(["foo" : 10])
;一个辅助 ctfe 函数,它采用内置文字并将其转换为您的格式),但由于运算符重载,用法基本相同。
当然,实现你自己的 AA 可能需要一些代码,也许不值得,但如果 CT 初始化是必须的,这是让它工作的方法。
(我个人觉得静态构造函数已经足够好了...)