Descubra como as bibliotecas compartilhadas em Go podem melhorar a eficiência de desenvolvimento de software em diversas linguagens.
Tempo de Leitura: 8 minutos
Bibliotecas compartilhadas (ou "shared libraries") são componentes onde permitem que múltiplos programas utilizem o mesmo conjunto de funções e recursos, promovendo a reutilização de código, a modularidade e a eficiência na utilização de memória. Este artigo aborda detalhadamente o conceito de bibliotecas compartilhadas, como criar uma em Go, conhecida por sua eficiência e simplicidade, proporcionando um conjunto robusto de ferramentas para a criação de bibliotecas compartilhadas que podem ser utilizadas por diversas outras linguagens, incluindo C, Python, e Java.
A popularidade de Go deve-se à sua sintaxe concisa, gerenciamento eficiente de memória e suporte nativo à concorrência, tornando-o uma escolha ideal para o desenvolvimento de software de alto desempenho e confiabilidade. A capacidade de criar bibliotecas compartilhadas expande ainda mais o potencial da linguagem, permitindo que desenvolvedores integrem funcionalidades Go em projetos existentes escritos em outras linguagens. Isso não só preserva os investimentos em código legado, mas também tira proveito das características avançadas de Go.
Ao final, você terá uma compreensão profunda das vantagens das bibliotecas compartilhadas e dos passos práticos para implementá-las em diferentes contextos.
O que é uma Biblioteca Compartilhada?
Uma biblioteca compartilhada é um arquivo que contém código e dados que podem ser utilizados por múltiplos programas simultaneamente. Elas são diferentes das bibliotecas estáticas, que são incorporadas diretamente nos programas no momento da compilação. Bibliotecas compartilhadas são carregadas na memória apenas uma vez, e diferentes programas podem acessar essas funções durante a execução sendo um arquivo que contém código executável. Em sistemas UNIX, as bibliotecas compartilhadas são geralmente chamadas de "shared objects" (arquivos .so), enquanto no Windows são conhecidas como "dynamic-link libraries" (arquivos .dll).
As bibliotecas compartilhadas surgiram para resolver problemas de redundância e para melhorar a gestão de memória. Nos primeiros sistemas operacionais, cada programa precisava incorporar suas próprias cópias das funções comuns, o que resultava em desperdício de memória e dificultava a manutenção do software, pois cada linguagem deveria ser desenvolver suas respectivas bibliotecas. Com a introdução das bibliotecas compartilhadas, tornou-se possível centralizar a atualização de código em uma única linguagem e economizar recursos do sistema.
Vantagens das Bibliotecas Compartilhadas
1. Economia de Memória
A memória utilizada é reduzida porque múltiplos programas podem compartilhar a mesma biblioteca, carregada uma única vez na memória.
2. Modularidade
Facilita a separação de funcionalidades em diferentes módulos, tornando o código mais organizado e fácil de manter.
3. Manutenção
Atualizações em bibliotecas compartilhadas podem ser feitas independentemente do programa principal. Ao corrigir um bug ou melhorar uma funcionalidade na biblioteca, todos os programas que a utilizam se beneficiam automaticamente dessa atualização.
4. Desempenho
O carregamento dinâmico de bibliotecas pode melhorar o desempenho do sistema, especialmente em ambientes onde a memória é um recurso crítico.
5. Portabilidade
As bibliotecas compartilhadas permitem que desenvolvedores criem componentes reutilizáveis que podem ser distribuídos e utilizados em diferentes aplicações, promovendo a consistência e a portabilidade do código.
Criando uma Biblioteca Compartilhada em Go
Go, também conhecido como Golang, é uma linguagem de programação desenvolvida pelo Google, conhecida por sua simplicidade e eficiência. Ela possui suporte nativo para a criação de bibliotecas compartilhadas. Neste guia, vamos criar uma biblioteca compartilhada simples em Go e demonstrar como utilizá-la em outras linguagens.
Para saber mais sobre Go, leia esse artigo
Passo a Passo
1. Criar o Código Go
Primeiramente, vamos criar o código Go, usarei o VSCode, que desejamos exportar. Suponhamos que queremos criar uma biblioteca que forneça uma função para somar dois números. Crie um arquivo chamado mathlib.go com o seguinte conteúdo:
Para isso iremos criar o go mod do projeto, eu seguirei usando a normalização da nomenclatura usando o caminho do repositório git Ex:
go mod init github.com/Thiagohalves85/sharedLibrary
Ele irá gerar um arquivo go.mod com as informações do pacote.
Com o nosso gerenciador de pacotes criado, vamos criar o arquivo main.go nele irá conter a lógica da nossa DLL
package main import "C" import "strings" //export Somar func Somar(a, b int) int { return a + b } //export Subtrair func Subtrair(a, b int) int { return a - b } //export Multiplicar func Multiplicar(a, b int) int { return a * b } //export Dividir func Dividir(a, b float64) float64 { return a / b } //export ConcatStrings func ConcatStrings(s1 *C.char, s2 *C.char) *C.char { goStr1 := C.GoString(s1) goStr2 := C.GoString(s2) result := goStr1 + goStr2 return C.CString(result) } //export StringLength func StringLength(s *C.char) C.int { goStr := C.GoString(s) return C.int(len(goStr)) } //export ToUpperCase func ToUpperCase(s *C.char) *C.char { goStr := C.GoString(s) result := strings.ToUpper(goStr) return C.CString(result) } //export ReverseString func ReverseString(s *C.char) *C.char { goStr := C.GoString(s) runes := []rune(goStr) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } return C.CString(string(runes)) } func main() {}
Explicação do Código
-
- Pacote main: Para compilar uma biblioteca compartilhada em Go, o pacote deve ser main.
- Diretiva import "C": Esta diretiva permite o uso de tipos e funções C no código Go.
- Funções Exportadas: está anotada com //export <Nome da Função>, indicando que ela deve ser acessível a partir de código externo.
- Função main vazia: Necessária para o pacote main.
2. Compilar para Biblioteca Compartilhada
Para compilar o código Go como uma biblioteca compartilhada, utilizamos o comando go build com as flags apropriadas. Execute o seguinte comando no terminal: go build -o sharedLibrary.dll -buildmode=c-shared main.go
Este comando gera dois arquivos: sharedLibrary.dll (a biblioteca compartilhada para arquitetura windows) e sharedLibrary.h (o arquivo de cabeçalho necessário para uso em outras linguagens ex: C).
Caso esteja programando em uma arquitetura Linux, poderá usar o seguinte comando: go build -o sharedLibrary.so -buildmode=c-shared main.go
Este comando irá gerar o arquivo sharedLibrary.h e também o arquivo sharedLibrary.so (esse arquivo é a biblioteca compartilhada para ser usada na arquitetura Linux)
Utilizando a Biblioteca em Outras Linguagens
Uma das principais vantagens das bibliotecas compartilhadas é sua capacidade de serem usadas por diferentes linguagens de programação. Vamos explorar como utilizar nossa biblioteca sharedLibrary.dll em Python e Java.
1. Utilizando a Biblioteca em Python
Python é uma linguagem de programação amplamente utilizada devido à sua simplicidade e versatilidade. Para utilizar a biblioteca compartilhada em Python, podemos usar a biblioteca ctypes, que facilita a integração com bibliotecas escritas em C.
Exemplo em Python
Primeiramente, certifique-se de que o arquivo sharedLibrary.dll está no mesmo diretório que o script Python. Crie um arquivo chamado test_sharedLibrary.py com o seguinte conteúdo:
import ctypes # Carregar a biblioteca compartilhada lib = ctypes.CDLL('./sharedLibrary.dll') # Definir os tipos dos argumentos e do retorno da função Somar lib.Somar.argtypes = (ctypes.c_int, ctypes.c_int) lib.Somar.restype = ctypes.c_int # Definir os tipos dos argumentos e do retorno da função Subtrair lib.Subtrair.argtypes = (ctypes.c_int, ctypes.c_int) lib.Subtrair.restype = ctypes.c_int # Definir os tipos dos argumentos e do retorno da função Multiplicar lib.Multiplicar.argtypes = (ctypes.c_int, ctypes.c_int) lib.Multiplicar.restype = ctypes.c_int # Definir os tipos dos argumentos e do retorno da função Dividir lib.Dividir.argtypes = (ctypes.c_double, ctypes.c_double) lib.Dividir.restype = ctypes.c_double print("Entrando nas funções numericas") print("") # Chamar a função e imprimir o resultado Somar result = lib.Somar(3, 4) print(f"Resultado de 3 + 4: {result}") # Chamar a função e imprimir o resultado Subtrair result = lib.Subtrair(10, 4) print(f"Resultado de 10 - 4: {result}") # Chamar a função e imprimir o resultado Multiplicar result = lib.Multiplicar(3, 4) print(f"Resultado de 3 x 4: {result}") # Chamar a função e imprimir o resultado Dividir result = lib.Dividir(12, 4) print(f"Resultado de 12 / 4: {result}") print("") print("Entrando nas funções de texto") print("") # Definir as assinaturas das funções lib.ConcatStrings.restype = ctypes.c_char_p lib.ConcatStrings.argtypes = [ctypes.c_char_p, ctypes.c_char_p] lib.StringLength.restype = ctypes.c_int lib.StringLength.argtypes = [ctypes.c_char_p] lib.ToUpperCase.restype = ctypes.c_char_p lib.ToUpperCase.argtypes = [ctypes.c_char_p] lib.ReverseString.restype = ctypes.c_char_p lib.ReverseString.argtypes = [ctypes.c_char_p] # Usar as funções da biblioteca s1 = b"Hello, " s2 = b"World!" s3 = b"trabalhando com bibliotecas compartilhadas" concat_result = lib.ConcatStrings(s1, s2) print(f"Palavra concatenada: {concat_result.decode('utf-8')}") length_result = lib.StringLength(s3) print(f"Tamanho da palavra '{s3.decode('utf-8')}': {length_result}") uppercase_result = lib.ToUpperCase(s3) print(f"Palavra maiuscula: {uppercase_result.decode('utf-8')}") reverse_result = lib.ReverseString(s3) print(f"Palavra Revertida: {reverse_result.decode('utf-8')}") print("")
Explicação do Código
- ctypes.CDLL: Carrega a biblioteca compartilhada.
- argtypes: Define os tipos dos argumentos ctypes.c_int que a função espera (neste caso, dois inteiros).
- restype: Define o tipo de retorno da função.
- lib.Somar(3, 4): Chama a função Somar com os argumentos 3 e 4, e o resultado é armazenado na variável result_somar.
- as funções seguintes (Subtrair e Multiplicar) seguem as mesmas logicas;
- Se for notar, tem uma diferença na função Dividir nela foi utilizada float64 na DLL Go e nos argumentos do Python foi utilizado ctypes.c_double, para ter um retorno de número seja flutuante.
- Para as funções de texto, no Go usamos a variável *C.char e no Python passamos os argumentos ctypes.c_char_p
Agora se fizer o comando: python3 test_sharedLibrary.py deverá retornar assim:
Entrando nas funções numericas
Resultado de 3 + 4: 7
Resultado de 10 - 4: 6
Resultado de 3 x 4: 12
Resultado de 12/4: 3.0
Entrando nas funções de texto
Palavra concatenada: Hello, World!
Tamanho da palavra 'trabalhando com bibliotecas compartilhadas': 42
Palavra maiúscula: TRABALHANDO COM BIBLIOTECAS COMPARTILHADAS
Palavra Revertida: sadahlitrapmoc sacetoilbib moc odnahlabart
2. Chamar a DLL a partir do Java
Para chamar a DLL a partir do Java, usaremos a biblioteca JNA (Java Native Access).
Código Java
- Adicionar JNA ao seu projeto:
Se estiver usando Maven, adicione a dependência JNA no seu pom.xml:
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.13.0</version> </dependency>
- Escrever o código Java para chamar a função DLL:
Crie uma classe SharedLibrary.java e adicione o seguinte código:
import com.sun.jna.Library; import com.sun.jna.Native; public interface SharedLibrary extends Library { //criamos a instancia da classe nativa que chama a biblioteca SharedLibrary INSTANCE = (SharedLibrary) Native.load("sharedLibrary", SharedLibrary.class); //Chama a função Somar da biblioteca Integer Somar(Integer num1, Integer num2); //Chama a função Subtrair da biblioteca Integer Subtrair(Integer num1, Integer num2); //Chama a função Multiplicar da biblioteca Integer Multiplicar(Integer num1, Integer num2); //Chama a função Dividir da biblioteca double Dividir(double num1, double num2); //Chama a função ConcatString da biblioteca String ConcatStrings(String st1, String st2); //Chama a função StringLength da biblioteca Integer StringLength(String st1); //Chama a função ToUpperCase da biblioteca String ToUpperCase(String st1); //Chama a função ReverseString da biblioteca String ReverseString(String st1); }
Explicação do Código
- SharedLibrary INSTANCE = (SharedLibrary) Native.load("sharedLibrary", SharedLibrary.class): Carrega a biblioteca compartilhada.
- Após criamos as funções idênticas às que foram criadas na biblioteca em Go, se na biblioteca estiver com *C.char em Java será String, se for float64 em Go será double no Java e int será Integer no Java.
- Agora adicionar o seguinte codigo no App.Java:
public class App { public static void main( String[] args ) { System.out.println("Entrando nas funções numericas"); System.out.println(""); //Cria um objeto da classe aonde foi instanciado a biblioteca SharedLibrary Lib = SharedLibrary.INSTANCE; //Passa as variaveis para o objeto chamando a função Somar Integer resultado = Lib.Somar(3,4); System.out.println("Resultado de 3 + 4: " + resultado); //Passa as variaveis para o objeto chamando a função Subtrair Integer resultado2 = Lib.Subtrair(10,4); System.out.println("Resultado de 10 - 4: " + resultado2); //Passa as variaveis para o objeto chamando a função Multiplicar Integer resultado3 = Lib.Multiplicar(3,4); System.out.println("Resultado de 3 x 4: " + resultado3); //Passa as variaveis para o objeto chamando a função Dividir double resultado4 = Lib.Dividir(12, 4); System.out.println("Resultado de 12 / 4: " + resultado4); System.out.println(""); System.out.println("Entrando nas funções de texto"); String s1 = "Hello, "; String s2 = "World!"; String s3 = "trabalhando com bibliotecas compartilhadas"; //Passa as variaveis para o objeto chamando a função ConcatStrings String concat_result = Lib.ConcatStrings(s1,s2); System.out.println("Palavra concatenada: " + concat_result); //Passa as variavel para o objeto chamando a função StringLength Integer length_result = Lib.StringLength(s3); System.out.println("Tamanho da palavra " + s3 + ": " + length_result); //Passa as variavel para o objeto chamando a função ToUpperCase String uppercase_result = Lib.ToUpperCase(s3); System.out.println("Palavra maiuscula: " + uppercase_result); //Passa as variavel para o objeto chamando a função ReverseString String reverse_result = Lib.ReverseString(s3); System.out.println("Palavra Revertida: " + reverse_result); } }
Ao executar o comando deverá retornar assim:
Entrando nas funções numéricas
Resultado de 3 + 4: 7
Resultado de 10 - 4: 6
Resultado de 3 x 4: 12
Resultado de 12 / 4: 3.0
Entrando nas funções de texto
Palavra concatenada: Hello, World!
Tamanho da palavra trabalhando com bibliotecas compartilhadas: 42
Palavra maiuscula: TRABALHANDO COM BIBLIOTECAS COMPARTILHADAS
Palavra Revertida: sadahlitrapmoc sacetoilbib moc odnahlabart
PS C:\Users\thiago.alves\Documents\Estudo\Java\nfe>
Conclusão
Bibliotecas Compartilhadas (Shared libraries) são ferramentas poderosas para compartilhar código entre diferentes programas e linguagens. Go facilita a criação de shared libraries que podem ser integradas com linguagens como C, Python, Java, etc.
Neste artigo, vimos como criar uma shared library em Go e como integrá-la em um script Python e na linguagem Java. Essa flexibilidade pode ser extremamente útil em projetos que exigem interoperabilidade entre diferentes linguagens de programação de maneira eficiente.
Ao longo deste artigo, cobrimos os fundamentos necessários para começar a desenvolver shared libraries em Go, desde a configuração do ambiente até a implementação e uso em outras linguagens de programação.