Golang utiliza la tecnología CGO&Plugin para cargar la biblioteca dinámica C en tiempo de ejecución

Introducción al artículo

Este documento presenta una tecnología para cargar la biblioteca dinámica C en el programa Golang en tiempo de ejecución, omite el proceso de vincular la biblioteca dinámica C en la fase de compilación del proyecto Golang y mejora la flexibilidad del desarrollo y la implementación del proyecto Golang.

experiencia técnica

El programa Golang llama a las funciones de la biblioteca dinámica OpenCC para realizar la operación de convertir chino tradicional a texto simplificado. Es necesario no vincular la biblioteca dinámica en tiempo de compilación y solo cargar la biblioteca dinámica OpenCC cuando el programa se está ejecutando.

La biblioteca OpenCC es un programa de conversión tradicional y simplificado escrito en C++ y proporciona una interfaz API en lenguaje C. Dirección del proyecto de código abierto: github.com/BYVoid/Open…

La tecnología CGO es una forma de permitir que el lenguaje Golang use código de lenguaje C, que puede vincular la biblioteca dinámica C cuando se compila el programa Golang. Después de décadas de desarrollo, el lenguaje C tiene una gran cantidad de proyectos de código abierto. Cuando el ecosistema de Golang no es muy completo, a menudo necesitamos usar algunos proyectos maduros de código abierto C.

El complemento es un nuevo sistema de complementos introducido en la versión 1.8 de Golang, que permite a los programadores crear programas modulares acoplados libremente utilizando bibliotecas de enlaces dinámicos, que se cargan y enlazan dinámicamente cuando el programa se está ejecutando.

Este artículo explica 2 soluciones paso a paso:

Solución 1: use la tecnología CGO para vincular la interfaz OpenCC en tiempo de compilación.

Solución 2: Introducir la tecnología de complementos para cargar la biblioteca dinámica cuando el programa se está ejecutando.

Solución 1

El esquema 1 es la solución original, que usa tecnología CGO y vincula la interfaz OpenCC en tiempo de compilación.

Golang -> CGO -> libopencc.so

Escriba el archivo de definición de la interfaz CGO:

// opencc.h
#include <stdlib.h>

void* Opencc_New(const char *configFile);
void* Opencc_Delete(void *id);
const char *Opencc_Convert(void *id, const char *input);
void Opencc_Free_String(char *p);
复制代码
// opencc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "opencc/src/opencc.h"

const char *Convert(const char *input, const char *config) {
        if(strlen(config) > 16) {
                return 0;
        }

        char configFile[256] = "/usr/share/opencc/";
        strcat(configFile, config);
        strcat(configFile, ".json");

        opencc_t p = opencc_open(configFile);
        char *out = opencc_convert_utf8(p, input, strlen(input));
        out[strlen(input)] = '\0';

        opencc_close(p);

        return out;
}

void Convert_free_string(char *p) {
        opencc_convert_utf8_free(p);
}

void* Opencc_New(const char *configFile) {
        return opencc_open(configFile);
}

void Opencc_Delete(void *id) {
        opencc_close(id);
}

const char *Opencc_Convert(void *id, const char *input) {
        char *output = opencc_convert_utf8(id, input, strlen(input));
        output[strlen(input)] = '\0';
        return output;
}

void Opencc_Free_String(char *p) {
        opencc_convert_utf8_free(p);
}
复制代码
// opencc.go
package opencc

import "unsafe"
// #cgo LDFLAGS: -L${SRCDIR}/output/lib -lopencc
// #include "opencc.h"

import "C"

func NewConverter(config string) unsafe.Pointer {
        return C.Opencc_New(C.CString(config))
}

func Convert(p unsafe.Pointer, input string) string {
        inputChars := C.CString(input)
        outputChars := C.Opencc_Convert(p, inputChars)

        defer C.Opencc_Free_String(inputChars)
        defer C.Opencc_Free_String(outputChars)

        result := C.GoString(outputChars)
        return result
}

func Close(p unsafe.Pointer) {
        C.Opencc_Delete(p)
}

func ConvertOneTime(input string, config string) string {
        p := NewConverter(config)
        defer Close(p)
        return Convert(p, input)
}

func ConvertAsync(input string, config string, callback func(output string)) {
        go func() {
                callback(ConvertOneTime(input, config))
        }()
}
复制代码
// test.go 调用OpenCC动态库的函数。
package main

import "fmt"
import "your/repository/go-opencc"

const (
        input = "中国鼠标软件打印机"
        config_s2t = "/usr/share/opencc/s2t.json"
        config_t2s = "/usr/share/opencc/t2s.json"
)

func main() {
        fmt.Println("Test Converter class:")
        c := opencc.NewConverter(config_s2t)
        defer c.Close()
        output := c.Convert(input)
        fmt.Println(output)

        fmt.Println("Test Convert function:")
        s := opencc.Convert(input, config_s2t)
        fmt.Println(s)
        fmt.Println(opencc.Convert(s, config_t2s))

        fmt.Println("Test ConvertAsync function:")
        retChan := make(chan string)
        opencc.ConvertAsync(input, config_s2t, func(output string) {
                retChan <- output
        })

        fmt.Println(<- retChan)
}
复制代码

Opción 1, el archivo libopencc.so se puede vincular correctamente y la función Convertir se puede ejecutar con éxito para realizar una conversión tradicional y simplificada. Pero hay un problema, el programa no es fácil de compilar en el sistema Mac y el proyecto OpenCC debe instalarse en el sistema Mac. Si el proyecto que llama OpenCC es desarrollado conjuntamente por 10 personas, debe compilarse en la computadora Mac de 10 personas, lo que es difícil para la colaboración en el desarrollo e inconveniente para la implementación.

Solución 2

Se introduce la tecnología de complementos y la biblioteca dinámica se carga cuando se ejecuta el programa.

Golang -> Complemento -> libgo_opencc.so -> CGO -> libopencc.so

Escriba la biblioteca de enlaces dinámicos del complemento.

// opencc_lib.go
package main

import (
        "unsafe"

        opencc "your/repository/go-opencc"
)

type openccConverter string

// NewConverter 创建Converter
func (s openccConverter) NewConverter(config string) unsafe.Pointer {
        return opencc.NewConverter(config)
}

// Convert 转换函数
func (s openccConverter) Convert(p unsafe.Pointer, input string) string {
        return opencc.Convert(p, input)
}

// Close 释放Converter占用的内存资源(不再使用Converter了)
func (s openccConverter) Close(p unsafe.Pointer) {
        opencc.Close(p)
}

// ConvertOneTime 转换函数(转换一次,该函数每次调用都会加载配置文件,有性能影响)
func (s openccConverter) ConvertOneTime(input string, config string) string {
        return opencc.ConvertOneTime(input, config)
}

// OpenccConverter export symble
var OpenccConverter openccConverter
复制代码

Compile la biblioteca dinámica build.sh para crear el directorio de salida y compile y genere la biblioteca dinámica ./output/lib/libgo_opencc.so.

#!/bin/bash

mkdir -p output

cd opencc
./build.sh
cd ..

cp -rf opencc/output/* ./output

go build -buildmode=plugin -o ./output/lib/libgo_opencc.so ./lib/opencc_lib.go
复制代码

Use el complemento para cargar libgo_opencc.so y llamar a las funciones de OpenCC.

package main

import (
        "os"
        "plugin"
        "testing"
        "unsafe"
        "fmt"
)

// 实现 opencc_lib.go 的接口
type OpenccConverter interface {
        NewConverter(config string) unsafe.Pointer
        Convert(p unsafe.Pointer, input string) string
        Close(p unsafe.Pointer)
        ConvertOneTime(input string, config string) string
}

func TestOpenccSo(t *testing.T) {
        openccPlugin, pluginErr := plugin.Open("/your/path/to/go-opencc/output/lib/libgo_opencc.so")
        if pluginErr != nil {
                panic(pluginErr)
        }

        symbol, cerr := openccPlugin.Lookup("OpenccConverter")
        if cerr != nil {
                fmt.Errorf("%+v", cerr)
                os.Exit(1)
        }

        plug, ok := symbol.(OpenccConverter)
        if !ok {
                fmt.Errorf("unexpected type from module symbol")
                os.Exit(1)
        }

        config := "/usr/share/opencc/hk2s.json"
        pointer := plug.NewConverter(config)
        defer plug.Close(pointer)

        input := "傳統漢字"

        output := plug.Convert(pointer, input)

        fmt.Printf("output: %s", output)
}

// 运行测试 TestOpenccSo,输出 “传统汉字”。
复制代码

La opción 2 realiza la carga de la biblioteca dinámica libgo_opencc.so a través de la tecnología Plugin cuando el programa se está ejecutando y luego vincula libgo_opencc.so a libopencc.so para compilar programas Golang en sistemas Mac y Linux sin implementar OpenCC en cada proyecto de máquina de desarrollo. Cuando finalmente se lance al entorno de producción, libgo_opencc.so y libopencc.so serán empaquetados y liberados por la plataforma de compilación y empaquetado.

Supongo que te gusta

Origin juejin.im/post/7120176632693784607
Recomendado
Clasificación