Golang中如何创建静态/硬编码的自动生成键值对(即映射)?

Golang中如何创建静态/硬编码的自动生成键值对(即映射)? 将一个应用程序从Python移植到Go。我需要解析.h头文件并自动生成键值对,在Go语言中称为映射。但这些映射必须是静态的,因为生成这些键值对的代码只运行一次。这个代码的Python实现只是创建一个.py文件,其中定义了包含键/值的字典。

在Go中使用映射的类似实现会是什么样?或者简单地创建一个JSON文件会更顺畅吗?

基本上,在Go语言中是否可以实现硬编码的映射?

编辑:所以我的想法是,每次程序运行时,使用init()来初始化自动生成的硬编码映射。

5 回复

感谢您的详细回复,双映射的方法非常巧妙。

更多关于Golang中如何创建静态/硬编码的自动生成键值对(即映射)?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢你的回复,skillian。基本上,是的,一个包含值的全局映射。就像下图所示,基本上,一个生成的Go文件,其中包含硬编码的值,并在我的主函数中使用init,应该就能工作。

类似下图所示:

hard coded key vals

你能澄清一下“静态”具体指什么吗?

如果你指的是硬编码在程序中的不可变键值对,那么我认为你需要的是 const 常量。不过它们有限制,你只能使用像 boolintfloat32string 等标量值。不能使用切片、结构体、数组等。

如果你的值不仅仅是简单的字符串或数字,并且/或者你所说的“静态”是指类似 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):

  1. 创建解析.h文件的工具
  2. 生成包含硬编码映射的.go文件
  3. 使用 go:generate 自动化流程
  4. 生成的映射在编译时确定,运行时无需解析

这种方法结合了Python方案的灵活性(自动生成)和Go的编译时安全性(类型检查)。

回到顶部