如何在编译时使用四个 ASCII 代码创建常量 uint32 标签?
How to create constant uint32 tags with four ASCII codes at compile time?
我需要创建许多映射 ASCII 字母的 uint32 标签。例如,标签“abcd”被编码为 0x61626364,其中每个字节对应于字母的 ASCII 代码。
一个简单的解决方案是像这样定义标签值
type Tag uint32
const Tag_abcd = Tag(0x61626364)
但这很容易出错。
一个不太容易出错的解决方案是使用接收字母作为参数的函数来定义标签值。
const Tag_abcd = foo("abcd")
或者像这样可以用 C 中的宏轻松完成
const Tag_abcd = bar('a','b','c','d')
但这需要支持在编译时求值的函数。据我所知,Go 是不可能的。我对么 ?还有其他方法吗?
您可以使用 go generate 将复合常量值解析为不同的内存分配。
维护此实现非常复杂,但如果您的团队立即需要知道它的作用以及何时可以安全地重构它
您可以 assemble 常量使用 rune literals and bit shifts. It won't be too compact, but it will be "safe" (meaning you can see the characters in the constant expression):
const TagABCD Tag = 'a'<<24 + 'b'<<16 + 'c'<<8 + 'd'
或者你可以写成多行,这样字母在一列中对齐:
const TagABCD2 Tag = 0 +
'a'<<24 +
'b'<<16 +
'c'<<8 +
'd'
要扩展 icza 的答案并提高标签声明的可读性,您可以:
- 以
<letter><number>
的形式声明辅助常量,其中 <letter>
是相关的 ASCII 字符,<number>
是该字符在 4 字符标记中的位置。
- 将符文位移
iota
* 8
- 通过 OR
|
辅助常量 组成标签常量
import "fmt"
const (
a1 uint32 = 'a'<<(iota*8)
a2
a3
a4
)
// other similar const declarations for b1,b2,b3,b4 and so on
// must repeat the keyword const to reset iota
const Tag_abcd = a4 | b3 | c2 | d1
const Tag_ddba = d4 | d3 | b2 | a1
func main() {
fmt.Printf("%x\n", Tag_abcd) // 61626364
fmt.Printf("%x\n", Tag_ddba) // 64646261
}
优点是:
- 标记声明对于维护人员来说可能更容易阅读也更直接
- 借助 IDE 支持
可以轻松重构助手标识符
缺点是:
- 源代码可能会变得更加冗长,但您可以通过将辅助常量隔离到单独的文件中来缓解这种情况
- 对于大写 ASCII,辅助常量如
A1
将被导出,因此您可能必须在标识符前加上 _
或类似技巧
YMMV
我需要创建许多映射 ASCII 字母的 uint32 标签。例如,标签“abcd”被编码为 0x61626364,其中每个字节对应于字母的 ASCII 代码。
一个简单的解决方案是像这样定义标签值
type Tag uint32
const Tag_abcd = Tag(0x61626364)
但这很容易出错。
一个不太容易出错的解决方案是使用接收字母作为参数的函数来定义标签值。
const Tag_abcd = foo("abcd")
或者像这样可以用 C 中的宏轻松完成
const Tag_abcd = bar('a','b','c','d')
但这需要支持在编译时求值的函数。据我所知,Go 是不可能的。我对么 ?还有其他方法吗?
您可以使用 go generate 将复合常量值解析为不同的内存分配。
维护此实现非常复杂,但如果您的团队立即需要知道它的作用以及何时可以安全地重构它
您可以 assemble 常量使用 rune literals and bit shifts. It won't be too compact, but it will be "safe" (meaning you can see the characters in the constant expression):
const TagABCD Tag = 'a'<<24 + 'b'<<16 + 'c'<<8 + 'd'
或者你可以写成多行,这样字母在一列中对齐:
const TagABCD2 Tag = 0 +
'a'<<24 +
'b'<<16 +
'c'<<8 +
'd'
要扩展 icza 的答案并提高标签声明的可读性,您可以:
- 以
<letter><number>
的形式声明辅助常量,其中<letter>
是相关的 ASCII 字符,<number>
是该字符在 4 字符标记中的位置。 - 将符文位移
iota
* 8
- 通过 OR
|
辅助常量 组成标签常量
import "fmt"
const (
a1 uint32 = 'a'<<(iota*8)
a2
a3
a4
)
// other similar const declarations for b1,b2,b3,b4 and so on
// must repeat the keyword const to reset iota
const Tag_abcd = a4 | b3 | c2 | d1
const Tag_ddba = d4 | d3 | b2 | a1
func main() {
fmt.Printf("%x\n", Tag_abcd) // 61626364
fmt.Printf("%x\n", Tag_ddba) // 64646261
}
优点是:
- 标记声明对于维护人员来说可能更容易阅读也更直接
- 借助 IDE 支持 可以轻松重构助手标识符
缺点是:
- 源代码可能会变得更加冗长,但您可以通过将辅助常量隔离到单独的文件中来缓解这种情况
- 对于大写 ASCII,辅助常量如
A1
将被导出,因此您可能必须在标识符前加上_
或类似技巧
YMMV