【问题标题】:D straight array indexed by an enum由枚举索引的 D 直数组
【发布时间】:2017-02-27 00:37:43
【问题描述】:

在我的 D 程序中,我有一个固定长度的只读数组,我希望按枚举类型索引该数组。

如果我这样做

static const my_struct_t aray[ my_enum_t ] = ... whatever ...;

my_enum_t index;

result = aray[ index ];

那么 GDC 生成的代码是巨大的,在数组被索引时充满了对运行时的调用。所以看起来好像数组被视为可变长度或关联数组(哈希表)或其他东西,无论如何都远离具有直接索引的固定长度的轻量级 C 样式数组。由于枚举具有固定的基数并且不能增长,并且我的值范围适中(我没有滥用关键字 enum 只是为了定义大量随机常量),所以我不知道为什么会发生这种情况。

我通过将行改为

解决了这个问题
static const my_struct_t aray[ my_enum_t.max + 1 ]

据我了解,这意味着方括号中的值只是整数类型的已知常量。由于索引现在根本不是枚举,我现在有一个由整数索引的数组,所以我失去了类型检查,我可以用任何随机整数类型变量索引它,而不是确保只有正确的(强)类型是用过。

我该怎么办?

在更一般的情况下,(愚蠢的例子)

static const value_t aray[ bool ] = blah

例如,我有一个在语义上完全合理的索引类型,但不仅仅是无类型的 size_t/int/uint,我想我会遇到同样的问题。

我不想说这是一个编译器设计问题。这当然是次优行为的情况。但是为了对编译器公平起见,究竟是什么告诉它数组是固定长度还是可变的,是稀疏的还是密集的?我想要两件事;索引和非可变长度的类型检查。实际上,在这种特殊情况下,数组是 const (我也可以放不可变的),所以它显然不能是可变长度的。但是对于具有可修改内容但长度固定的数组,您需要能够声明它是固定长度的。

【问题讨论】:

  • 这个数组的初始化确实是在compile-type完成的,声明行有一个rhs初始化列表,里面装满了已知的编译时常量值。
  • 事实上,作为缺乏类型检查的坏处的一个例子,最初我忘记了方括号中的+1,所以我得到的数组大小太小了。碰巧碰巧发现了这一点,因为我正在查看生成代码并看到了一条线索,因为为静态对象生成的名称。因此,我放入了一些静态断言,但对我来说没有什么可以自动化。
  • 我在用 Pascal (Ada?) 术语思考。
  • 专业做了大量的asm和C,现在慢慢从D开始。

标签: arrays indexing enums d


【解决方案1】:

V[K] name 是关联数组的语法,它确实会执行运行时调用等,即使类型仅限于少数值,例如 bool 或枚举。编译器可能可以对其进行优化,使其像 AA 一样作用于程序,同时将其实现为简单的固定长度数组,但事实并非如此;它对所有键类型都一视同仁。

我建议使用您开始的方法:T[enum.max + 1],但如果您想强制类型安全,则进行包装。如果只需要一个实例,则可以将索引重载设为静态:

enum Foo {
        one,
        two
}

struct struct_t {}

struct array {
        static private struct_t[Foo.max + 1] content;
        static struct_t opIndex(Foo idx) { return content[cast(int) idx]; }     
}

void main() {
        struct_t a = array[Foo.one];
}

然后,如果您想要更简单的重用,您可以将其泛化。

struct enum_array(Key, Value) {
        static private struct_t[Key.max + 1] content;
        static Value opIndex(Key idx) { return content[cast(int) idx]; }
}

alias array = enum_array!(Foo, struct_t);

或者,当然,你不需要让它静态,你也可以做一个常规实例,并初始化里面的内容等等。

【讨论】:

  • 我会在此基础上工作。非常感谢。静态存在是因为我让它不可变并且不希望堆栈上的数组或常量数据的多个副本。
  • 碰巧的是,在这种特殊情况下,整个事情都被 CTFE 吃掉并处理了,所以几乎所有东西都被扔掉了。这是因为内联后的扩展以及索引值总是被证明是已知的编译时常量的事实。所以整个事情就像一个样板模板,它在编译时将一个已知的枚举映射到一个已知的常量结构。 D 编译器在 CTFE 方面做得很好。
  • 我想我应该检查枚举是否稀疏,所以没有漏洞,上帝知道怎么做!在上面的例子中,我也应该从索引中减去 key_t.min / enum_t.min(在两个地方)?
  • 有保证的非稀疏枚举会很好。
  • 其他一些事情:枚举是签名还是未签名?看到 GDC 偶尔使用签名指令的生成代码时,我有一种讨厌的感觉(忘记确切的细节)。我是一个非常有经验的 asm 黑客。我通过对第一个枚举常量使用 =0u 的初始化程序来修复它,这似乎可以解决问题。大概你可以在枚举中有一个可怕的正负常数混合。我想从 C 继承了必要性。但不知何故,仅将枚举用于一堆随机常量似乎并没有多大作用。
【解决方案2】:

在 D 中,静态和动态数组都由 size_t 索引,就像它们在 C 和 C++ 中一样。而且你不能像在 C 或 C++ 中那样更改 D 中的索引类型。因此,在 D 中,如果您在数组声明中的方括号之间放置一个类型,则您定义的是关联数组而不是静态数组。如果你想要一个静态数组,你必须提供一个整数字面量或编译时常量,并且没有办法要求一个裸静态数组由具有size_t 或类型的基本类型的枚举类型索引隐式转换为size_t

如果你想要求你的静态数组被size_t以外的类型索引,那么你需要将它包装在一个结构或类中,并通过成员函数控制对静态数组的访问。您可以重载opIndex 以获取您的枚举类型并将您的结构类型视为静态数组。因此,效果应该实际上是您尝试将枚举类型放入静态数组声明中所做的事情,但它将是成员函数获取枚举值并用它调用静态数组,而不是对静态数组本身。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 2011-12-31
    • 1970-01-01
    相关资源
    最近更新 更多