Skip to content

Commit 58cb391

Browse files
Initial commit.
0 parents  commit 58cb391

File tree

8 files changed

+303
-0
lines changed

8 files changed

+303
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
*.so
3+
*.o
4+
app

Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM ubuntu:18.04
2+
3+
ARG GO=1.13.4
4+
5+
RUN apt update && apt upgrade -y
6+
RUN apt install -y wget make gcc
7+
8+
WORKDIR /tmp
9+
RUN wget https://dl.google.com/go/go${GO}.linux-amd64.tar.gz
10+
RUN tar -xvf go${GO}.linux-amd64.tar.gz
11+
RUN mv go /usr/local
12+
13+
RUN echo export GOROOT=/usr/local/go >> ~/.bashrc
14+
RUN echo export GOPATH=\$HOME/go >> ~/.bashrc
15+
RUN echo export PATH=\$GOPATH/bin:\$GOROOT/bin:\$PATH >> ~/.bashrc
16+
17+
WORKDIR /src

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.PHONY: all c go run env
2+
3+
TESTLIBPATH="./ctestlib"
4+
5+
all: c go run
6+
7+
env:
8+
docker build --tag cgo .
9+
docker run --rm -ti -v $(shell pwd):/src cgo
10+
11+
c:
12+
gcc -c -Wall -Werror -fpic -o ${TESTLIBPATH}/test.o ${TESTLIBPATH}/test.c
13+
gcc -shared -o ${TESTLIBPATH}/libtest.so ${TESTLIBPATH}/test.o
14+
15+
go:
16+
go build -o app *.go
17+
18+
run:
19+
./app

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# CGO Examples
2+
3+
Examples of calling C code from Go (including a test C library). Passing and getting numbers, strings, enums, structs, callbacks.
4+
5+
### Build and run Docker env
6+
```bash
7+
make env
8+
```
9+
10+
### Compile and run app
11+
```bash
12+
make
13+
```
14+
15+
See [Makefile](Makefile)
16+
17+
### Docs
18+
* https://golang.org/cmd/cgo/
19+
* https://blog.golang.org/c-go-cgo
20+
* https://github.com/golang/go/wiki/cgo
21+
* https://gist.github.com/zchee/b9c99695463d8902cd33
22+
* https://dev.to/mattn/call-go-function-from-c-function-1n3

ctestlib/test.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "test.h"
2+
3+
int sum(int a, int b) {
4+
return a + b;
5+
}
6+
7+
const char* get_string() {
8+
return "string sent from C";
9+
}
10+
11+
void print_string(char* a) {
12+
printf("string sent from Go: %s\n", a);
13+
}
14+
15+
void print_buffer(unsigned char *buf, size_t size) {
16+
for (uint i = 0; i < size; i++) {
17+
printf("%X", buf[i]);
18+
}
19+
printf("\n");
20+
}
21+
22+
int point_diff(point p) {
23+
return p.x - p.y;
24+
}
25+
26+
void pass_void_pointer(void *ptr) {
27+
printf("%d\n", *((int*)ptr));
28+
}
29+
30+
void generate_numbers(uint num, uint callback) {
31+
void evenNumberCallbackProxy(uint, int);
32+
33+
for (uint i = 0; i <= num; i++) {
34+
if (i % 2 == 0) {
35+
evenNumberCallbackProxy(callback, i);
36+
}
37+
}
38+
}
39+
40+
void user_action(uint callback) {
41+
void userCallbackProxy(uint);
42+
43+
for (int i = 0; i < 5; i++) {
44+
userCallbackProxy(callback);
45+
}
46+
}

ctestlib/test.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef _TESTLIB_H_
2+
#define _TESTLIB_H_
3+
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
7+
typedef struct point
8+
{
9+
int x;
10+
int y;
11+
} point;
12+
13+
enum status {
14+
PENDING,
15+
DONE,
16+
};
17+
18+
int sum(int a, int b);
19+
20+
const char* get_string();
21+
22+
void print_string(char* a);
23+
24+
void print_buffer(unsigned char *buf, size_t size);
25+
26+
int point_diff(point p);
27+
28+
void pass_void_pointer(void *ptr);
29+
30+
void generate_numbers(uint num, uint callback);
31+
32+
void user_action(uint callback);
33+
34+
#endif

main.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package main
2+
3+
/*
4+
#cgo CFLAGS: -I${SRCDIR}/ctestlib
5+
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/ctestlib
6+
#cgo LDFLAGS: -L${SRCDIR}/ctestlib
7+
#cgo LDFLAGS: -ltest
8+
9+
#include <test.h>
10+
*/
11+
import "C"
12+
import (
13+
"bytes"
14+
"fmt"
15+
"unsafe"
16+
)
17+
18+
type User struct {
19+
Username string
20+
Visits int
21+
}
22+
23+
type Status int
24+
25+
const (
26+
Pending Status = iota
27+
Done
28+
)
29+
30+
func evenNumberCallback(num int) {
31+
fmt.Println("odd number: ", num)
32+
}
33+
34+
func userCallback(user unsafe.Pointer) {
35+
u := (*User)(unsafe.Pointer(user))
36+
u.Visits++
37+
}
38+
39+
func main() {
40+
// Numbers
41+
fmt.Println("\nNumbers")
42+
43+
a := 1
44+
b := 2
45+
sum := int(C.sum(C.int(a), C.int(b)))
46+
fmt.Print(sum, "\n\n")
47+
48+
// Get string
49+
fmt.Println("Get string")
50+
getString := C.GoString(C.get_string())
51+
fmt.Println(getString)
52+
stringBytes := C.GoBytes(unsafe.Pointer(C.get_string()), 24)
53+
fmt.Println(stringBytes[0:bytes.Index(stringBytes, []byte{0})])
54+
fmt.Println()
55+
56+
// Send string
57+
fmt.Println("Send string")
58+
str := "lorem ipsum"
59+
cStr := C.CString(str)
60+
C.print_string(cStr)
61+
C.free(unsafe.Pointer(cStr))
62+
fmt.Println()
63+
64+
// Send bytes
65+
fmt.Println("Send byte array")
66+
data := []byte{1, 4, 2}
67+
cBytes := (*C.uchar)(unsafe.Pointer(&data[0]))
68+
cBytesLength := C.size_t(len(data))
69+
fmt.Print("bytes: ")
70+
C.print_buffer(cBytes, cBytesLength)
71+
fmt.Println()
72+
73+
// Struct
74+
fmt.Println("Get and pass struct")
75+
point := C.struct_point{}
76+
point.x = 0
77+
point.y = 2
78+
fmt.Println(point)
79+
fmt.Print(C.point_diff(point), "\n\n")
80+
81+
// Arbitrary data: unsafe.Pointer to void pointer
82+
fmt.Println("Pass void pointer")
83+
C.pass_void_pointer(unsafe.Pointer(&point.y))
84+
fmt.Println()
85+
86+
// Enum
87+
fmt.Println("Access enum")
88+
fmt.Print(C.enum_status(Pending) == C.PENDING, C.PENDING, C.DONE, "\n\n")
89+
90+
// Callback
91+
fmt.Println("Pass callback")
92+
c := registerCallback(evenNumberCallback, nil)
93+
C.generate_numbers(5, c)
94+
unregisterCallback(c)
95+
96+
// Callback with params
97+
user := User{
98+
Username: "johndoe",
99+
}
100+
cWithParams := registerCallback(userCallback, unsafe.Pointer(&user))
101+
C.user_action(cWithParams)
102+
unregisterCallback(cWithParams)
103+
fmt.Println(user)
104+
}

proxy.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package main
2+
3+
import "C"
4+
import (
5+
"sync"
6+
"unsafe"
7+
)
8+
9+
type (
10+
index uint8
11+
store map[index]interface{}
12+
callback struct {
13+
callback interface{}
14+
arg unsafe.Pointer
15+
}
16+
)
17+
18+
var (
19+
storage = make(store)
20+
mutex sync.Mutex
21+
)
22+
23+
//export evenNumberCallbackProxy
24+
func evenNumberCallbackProxy(i C.uint, num int) {
25+
c := getCallback(index(i))
26+
c.callback.(func(int))(num)
27+
}
28+
29+
//export userCallbackProxy
30+
func userCallbackProxy(i C.uint) {
31+
c := getCallback(index(i))
32+
c.callback.(func(unsafe.Pointer))(c.arg)
33+
}
34+
35+
func registerCallback(c interface{}, arg unsafe.Pointer) C.uint {
36+
mutex.Lock()
37+
i := index(len(storage))
38+
storage[i] = &callback{
39+
callback: c,
40+
arg: arg,
41+
}
42+
mutex.Unlock()
43+
44+
return C.uint(i)
45+
}
46+
47+
func getCallback(i index) *callback {
48+
mutex.Lock()
49+
defer mutex.Unlock()
50+
return storage[i].(*callback)
51+
}
52+
53+
func unregisterCallback(i C.uint) {
54+
mutex.Lock()
55+
delete(storage, index(i))
56+
mutex.Unlock()
57+
}

0 commit comments

Comments
 (0)