211 lines
No EOL
4.4 KiB
Markdown
211 lines
No EOL
4.4 KiB
Markdown
---
|
||
layout: default
|
||
title: Bridging between languages
|
||
categories: programming
|
||
---
|
||
We can cross call functions from languages that:
|
||
|
||
- Both are compilable → Strategy: Create a binary library and call function from it.
|
||
- Library in `interpreted` and the project in `compiled` → Strategy: Create an interpreter context, then load the files and execute them.
|
||
- Library in `compiled` and the project in `interpreted` → Strategy: Use `FFI`.
|
||
- Both are interpreted
|
||
- Probably give up. Bridging will be possible, but messy and complicated
|
||
- Create two programs and exchange data between them using pipe, sockets, message quesues, databses etc.
|
||
|
||
### Creating shared and static library in Go
|
||
|
||
An example code that is shared [`example.go`](https://github.com/artur-gurgul/codebook):
|
||
|
||
``` go
|
||
package main
|
||
|
||
import "C"
|
||
import "fmt"
|
||
|
||
//export SayHello
|
||
func SayHello(hello *C.char) {
|
||
fmt.Print(C.GoString(hello))
|
||
}
|
||
|
||
func main() {}
|
||
```
|
||
|
||
- The `main` function is neccecery to include into library, because the final product has to have for example the GC rutines.
|
||
- The comment starting from `//export {function name}` tells the comiler that this the function will be called from the outside.
|
||
|
||
#### Creating static library
|
||
|
||
```
|
||
go build -o example.a -buildmode=c-archive example.go
|
||
```
|
||
|
||
#### Creating dynamic library
|
||
|
||
```
|
||
go build -o example.dylib -buildmode=c-shared example.go
|
||
```
|
||
|
||
### Creating shared and static library in Swift
|
||
|
||
`point.swift`
|
||
```swift point.swift
|
||
public struct Point {
|
||
public let x: Int
|
||
public let y: Int
|
||
|
||
public init(x: Int, y: Int) {
|
||
self.x = x
|
||
self.y = y
|
||
}
|
||
}
|
||
```
|
||
|
||
and compile with command (module name is optional)
|
||
|
||
```sh
|
||
swiftc point.swift -emit-module -module-name Point -emit-library -static
|
||
```
|
||
|
||
**Using**
|
||
|
||
```swift
|
||
import Point
|
||
|
||
let p = Point(x: 4, y: 20)
|
||
print("Hello library!", p.x, p.y)
|
||
```
|
||
|
||
compile with
|
||
|
||
```sh
|
||
swiftc main.swift -L ./lib/ -I ./lib/ -lpoint
|
||
```
|
||
|
||
#### Dynamic library in Swift
|
||
|
||
```
|
||
swiftc point.swift -emit-module -emit-library
|
||
```
|
||
|
||
it produces
|
||
|
||
- `libpoint.a`
|
||
- `point.swiftdoc`
|
||
- `point.swiftmodule`
|
||
- `point.swiftsourceinfo`
|
||
|
||
Compile main program the same way as it has been down with the static one
|
||
|
||
Library searching paths `/usr/lib/`, `/usr/local/lib/`
|
||
|
||
**_Create package that emits library_**
|
||
|
||
```swift
|
||
// swift-tools-version:5.3
|
||
import PackageDescription
|
||
|
||
let package = Package(
|
||
name: "MyLibrary",
|
||
products: [
|
||
/// type: automatic, based on the environment
|
||
.library(name: "MyLibrary",
|
||
// type: .dynamic, .static
|
||
targets: ["MyLibrary"]
|
||
),
|
||
],
|
||
targets: [
|
||
.target(name: "MyLibrary", dependencies: []),
|
||
]
|
||
)
|
||
```
|
||
|
||
### Calling function from library in Go
|
||
|
||
First off we will create C++ library that we will use in out Go program.
|
||
File `example.cxx`:
|
||
|
||
```c++
|
||
#include <stdio.h>
|
||
|
||
extern "C" {
|
||
|
||
void PrintHello(const char* u) {
|
||
printf("Hello: %s\n", u);
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
And `example.hxx`:
|
||
|
||
```c++
|
||
#pragma once
|
||
void PrintHello(const char* u)
|
||
```
|
||
|
||
`extern "C" {}` informs the compiler that we want the function names to be preserved. That is, to not "mangle" the names as is done for C++ code:
|
||
|
||
#### Creating static library
|
||
|
||
```bash
|
||
clang++ -c -Wall -o lib.o ./example.cxx
|
||
ar rc ./libexample.a ./lib.o
|
||
```
|
||
|
||
#### Creating dynamic library
|
||
|
||
```bash
|
||
clang++ -dynamiclib -o libexample.dylib example.cxx
|
||
```
|
||
|
||
## Statically linking an example library in Go
|
||
|
||
```go
|
||
package main
|
||
|
||
// #cgo CFLAGS: -I.
|
||
// #cgo LDFLAGS: -L. -lexample
|
||
//
|
||
// #include <example.hxx>
|
||
import "C"
|
||
|
||
func main() {
|
||
C.PrintHello(C.CString("Hello Golang"))
|
||
}
|
||
```
|
||
|
||
The program is linked staticaly with libexample when you build it.
|
||
|
||
#### Example of using library with FFI in Ruby
|
||
|
||
```shell
|
||
gem install ffi
|
||
```
|
||
|
||
```ruby
|
||
require 'ffi'
|
||
module Example
|
||
extend FFI::Library
|
||
ffi_lib './example.dylib'
|
||
attach_function :SayHello, [:string]
|
||
end
|
||
```
|
||
|
||
```ruby
|
||
Example.SayHello("Hello")
|
||
```
|
||
|
||
More informations about FFI: [https://en.wikipedia.org/wiki/Foreign_function_interface](https://en.wikipedia.org/wiki/Foreign_function_interface)
|
||
|
||
#### Call shared library from Python
|
||
|
||
```python
|
||
import ctypes
|
||
libc = ctypes.CDLL('./example.dylib')
|
||
libc.SayHello("Hello")
|
||
```
|
||
|
||
## Interesting websites
|
||
|
||
- [https://blog.filippo.io/building-python-modules-with-go-1-5/](https://blog.filippo.io/building-python-modules-with-go-1-5/)
|
||
- [https://id-rsa.pub/post/go15-calling-go-shared-libs-from-firefox-addon/](https://id-rsa.pub/post/go15-calling-go-shared-libs-from-firefox-addon/) |