Golang中如何创建静态/硬编码的自动生成键值对(即映射)?
Golang中如何创建静态/硬编码的自动生成键值对(即映射)? 将一个应用程序从Python移植到Go。我需要解析.h头文件并自动生成键值对,在Go语言中称为映射。但这些映射必须是静态的,因为生成这些键值对的代码只运行一次。这个代码的Python实现只是创建一个.py文件,其中定义了包含键/值的字典。
在Go中使用映射的类似实现会是什么样?或者简单地创建一个JSON文件会更顺畅吗?
基本上,在Go语言中是否可以实现硬编码的映射?
编辑:所以我的想法是,每次程序运行时,使用init()来初始化自动生成的硬编码映射。
感谢您的详细回复,双映射的方法非常巧妙。
更多关于Golang中如何创建静态/硬编码的自动生成键值对(即映射)?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你能澄清一下“静态”具体指什么吗?
如果你指的是硬编码在程序中的不可变键值对,那么我认为你需要的是 const 常量。不过它们有限制,你只能使用像 bool、int、float32、string 等标量值。不能使用切片、结构体、数组等。
如果你的值不仅仅是简单的字符串或数字,并且/或者你所说的“静态”是指类似 C 语言中静态函数、变量等概念,那么全局变量可能才是你想要的。在这种情况下,第一步(虽然你可能可以一步完成)是从数据中生成 struct 定义,然后定义一个或多个全局变量来保存这些值。
根据你的编辑内容,听起来你只是想要一个包含所有值的全局映射。那可能也适合你。
@Pac23,有几种方法可以实现这一点,但最佳方式取决于你如何使用这些值。最简单的方法可能是定义两个映射,并按照你在 Python 中初始化的方式对它们进行初始化:
var byName = map[string]int{}
var byVal = map[int]string{}
func init() {
byName["IDS"] = 1413685296
byVal[1413685296] = "IDS"
byName["IRMAP_CACHE"] = 1459634265
byVal[1459634265] = "IRMAP_CACHE"
byName["FS"] = 1363163410
byVal[1363163410] = "FS"
// ...
}
但是,根据这些值的使用方式,我还有其他建议。如果你经常需要通过名称查找这些值,并且这些名称在代码的其他地方被引用,那么将它们转换为代码中的标识符是有意义的。
也就是说,如果你目前正在做类似这样的事情:
resource = acquire_resource(by_name["FS"])
在 Go 中,最好将这些名称转换为标识符,这样你就可以在编译时(甚至在代码测试之前)捕获诸如拼写错误之类的错误。
如果确实如此,我建议采用更抽象的方案,例如:
package main
import (
"fmt"
)
// nameValues is the global collection of NameValuePairs.
var (
nameValues = NewNameValues(1024)
IDS = nameValues.mustDefine(NameValuePair{Name: "IDS", Value: 1413685296})
IRMAP_CACHE = nameValues.mustDefine(NameValuePair{Name: "IRMAP_CACHE", Value: 1459634265})
FS = nameValues.mustDefine(NameValuePair{Name: "FS", Value: 1363163410})
// ...
)
// NameValuePair pairs together a Name and an integer value.
type NameValuePair struct {
Name string
Value int
}
// NameValues is a collection of NameValuePairs indexed by both
// name and value.
type NameValues struct {
// pairs is the actual collection of all of the name value pairs
pairs []NameValuePair
// names is a map of names to the index of the NameValuePair in
// the pairs slice.
names map[string]int
// values is a map of values to the index of the NameValuePair in
// the pairs slice.
values map[int]int
}
// NewNameValues creates a new NameValues collection.
func NewNameValues(capacity int) *NameValues {
return &NameValues{
pairs: make([]NameValuePair, 0, capacity),
names: make(map[string]int, capacity),
values: make(map[int]int, capacity),
}
}
func (nvs *NameValues) mustDefine(p NameValuePair) NameValuePair {
added := nvs.Add(p)
if !added {
panic("redefinition of pair name or value")
}
return p
}
// Add a NameValuePair to the collection if it doesn't conflict with an
// existing entry's name or value.
func (nvs *NameValues) Add(p NameValuePair) (added bool) {
i, ok := nvs.values[p.Value]
if ok {
return false
}
i, ok = nvs.names[p.Name]
if ok {
return false
}
i = len(nvs.pairs)
nvs.pairs = append(nvs.pairs, p)
nvs.names[p.Name] = i
nvs.values[p.Value] = i
return true
}
// ByName gets a NameValuePair by its name if it exists in the
// collection.
func (nvs *NameValues) ByName(name string) (NameValuePair, bool) {
i, ok := nvs.names[name]
if !ok {
return NameValuePair{}, false
}
return nvs.pairs[i], true
}
// ByValue gets a NameValuePair by its name if it exists in the
// collection.
func (nvs *NameValues) ByValue(value int) (NameValuePair, bool) {
i, ok := nvs.values[value]
if !ok {
return NameValuePair{}, false
}
return nvs.pairs[i], true
}
func main() {
fmt.Println("IDS:", IDS.Value)
fmt.Println("1459634265:", IRMAP_CACHE.Name)
}
这仍然假设标识符在程序的整个运行过程中不会改变。如果标识符是动态的,并且你需要通过字符串访问它们,那么使用双映射方法可能就足够了。
在Go中创建静态/硬编码的自动生成映射是完全可行的。以下是几种实现方式:
1. 使用代码生成创建硬编码映射
创建生成器工具(例如 generate_maps.go):
// generate_maps.go
package main
import (
"fmt"
"os"
"strings"
)
func main() {
// 解析.h头文件的逻辑(示例数据)
headerData := map[string]string{
"MAX_SIZE": "1024",
"VERSION": "1.0.0",
"TIMEOUT": "30",
"BUFFER_LEN": "256",
}
var builder strings.Builder
builder.WriteString("package main\n\n")
builder.WriteString("var HeaderConstants = map[string]string{\n")
for key, value := range headerData {
builder.WriteString(fmt.Sprintf("\t\"%s\": \"%s\",\n", key, value))
}
builder.WriteString("}\n")
// 写入生成的Go文件
os.WriteFile("generated_maps.go", []byte(builder.String()), 0644)
}
运行生成器后,会创建 generated_maps.go:
// generated_maps.go
package main
var HeaderConstants = map[string]string{
"MAX_SIZE": "1024",
"VERSION": "1.0.0",
"TIMEOUT": "30",
"BUFFER_LEN": "256",
}
2. 使用 go:generate 指令
在源文件中添加:
// main.go
package main
//go:generate go run generate_maps.go
func main() {
// 使用生成的映射
fmt.Println("Version:", HeaderConstants["VERSION"])
fmt.Println("Max Size:", HeaderConstants["MAX_SIZE"])
}
运行 go generate 命令自动生成代码。
3. 使用 init() 函数初始化
package main
import "fmt"
var HeaderConstants map[string]string
func init() {
HeaderConstants = make(map[string]string)
// 硬编码的值
HeaderConstants["MAX_SIZE"] = "1024"
HeaderConstants["VERSION"] = "1.0.0"
HeaderConstants["TIMEOUT"] = "30"
HeaderConstants["BUFFER_LEN"] = "256"
// 或者从解析.h文件的结果填充
// HeaderConstants = parseHeaderFile("config.h")
}
func main() {
fmt.Println("Constants:", HeaderConstants)
}
4. 使用常量映射模式
对于编译时常量,可以使用常量映射:
package main
import "fmt"
type ConfigKey string
const (
MaxSize ConfigKey = "MAX_SIZE"
Version ConfigKey = "VERSION"
Timeout ConfigKey = "TIMEOUT"
BufferLen ConfigKey = "BUFFER_LEN"
)
var configMap = map[ConfigKey]string{
MaxSize: "1024",
Version: "1.0.0",
Timeout: "30",
BufferLen: "256",
}
func main() {
fmt.Println(configMap[Version])
}
5. JSON文件方案
如果需要外部配置,可以生成JSON文件:
// 生成JSON
package main
import (
"encoding/json"
"os"
)
func generateJSON() {
data := map[string]string{
"MAX_SIZE": "1024",
"VERSION": "1.0.0",
"TIMEOUT": "30",
"BUFFER_LEN": "256",
}
jsonData, _ := json.MarshalIndent(data, "", " ")
os.WriteFile("config.json", jsonData, 0644)
}
// 使用JSON
package main
import (
"encoding/json"
"fmt"
"os"
)
func loadConfig() map[string]string {
data, _ := os.ReadFile("config.json")
var config map[string]string
json.Unmarshal(data, &config)
return config
}
推荐方案
对于你的用例(从.h文件生成,只运行一次),推荐使用代码生成方案(方案1或2):
- 创建解析.h文件的工具
- 生成包含硬编码映射的.go文件
- 使用
go:generate自动化流程 - 生成的映射在编译时确定,运行时无需解析
这种方法结合了Python方案的灵活性(自动生成)和Go的编译时安全性(类型检查)。


