Using strfmon with cgo - go

I'm trying to use the C function strfmon using cgo.
The example C code that works is:
#include <stdio.h>
#include <monetary.h>
int main(void)
{
char str[100];
double money = 1234.56;
strfmon(str, 100, "%i", money);
printf("%s\n", string);
}
The Go code I've written so far is:
package main
// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include <monetary.h>
import "C"
import (
"fmt"
)
func main() {
str := [100]C.char{}
var money C.double = 1234.56
C.strfmon(str, 100, "%i", money)
fmt.Printf("%+v\n", str)
}
When I go run main.go I get the following error:
./main.go:14:2: unexpected type: ...
I believe the ... refers to the variadic argument in strfmon but I'm not sure how to work around that from Go.

According to the cgo command documentation:
Calling variadic C functions is not supported. It is possible to circumvent this by using a C function wrapper.
And strfmon(3p) is indeed a variadic function as indicated by the ... characters in the signature:
ssize_t strfmon(char *restrict s, size_t maxsize,
const char *restrict format, ...);
As such, you can create a wrapper function in C which has a fixed number of arguments and calls strfmon(...) as needed, for example:
package main
// #cgo CFLAGS: -g -Wall
//
// #include <locale.h>
// #include <monetary.h>
// #include <stdlib.h>
//
// size_t format_amount(char * s, size_t maxsize, char * format, double amount)
// {
// setlocale(LC_ALL, "en_US");
// return strfmon(s, maxsize, format, amount);
// }
//
import "C"
import "fmt"
import "unsafe"
const SIZE = 100
func main() {
str := C.CString(string(make([]byte, SIZE)))
money := C.double(1234.56)
format := C.CString("[%n]")
C.format_amount(str, SIZE-1, format, money) // Call our wrapper here.
fmt.Printf("OK: %s\n", C.GoString(str))
// OK: [$1,234.56]
C.free(unsafe.Pointer(str))
C.free(unsafe.Pointer(format))
}

Related

Segmentation violation error when calling fts_open via cgo

I'm testing cgo and every simple hello world like code works well.
but i have a problem with C code below.
The C code is that traverse a directory tree and sums file size.
if i build with go command, then the build is OK with no error.
but when running, there is a "segmentation violation" error occurred
bash$./walkdir
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x1 pc=0x7f631e077c1a]
. . . .
-------------------------------------------------------------
package main
/*
#include <stdint.h>
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char *path)
{
uintmax_t total_size = 0;
FTS *fts = fts_open(&path, FTS_PHYSICAL, NULL);
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.get_total_size(C.CString("/usr")))
}
fts_open is defined like this:
fts_open()
The fts_open() function takes a pointer to an array of character
pointers naming one or more paths which make up a logical file
hierarchy to be traversed. The array must be terminated by a
null pointer.
C does not have direct support for arrays; it only has pointers.
In your case you pass fts_open a single valid pointer but it is not located in an array which has a NULL pointer as the immediately following element, so fts_open continues to scan the memory past &path — looking for a NULL pointer, — and eventually tries to read memory at some address it is forbidden to do so (usually because the page at that address was not allocated).
A way to fix it is to create that array and initialize it on the C side.
Looks like you're using a reasonably up-to-date standard of C, so let's just use direct literal to initialize the array:
package main
/*
#include <stddef.h> // for NULL
#include <stdint.h>
#include <stdlib.h> // for C.free
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char *path)
{
uintmax_t total_size = 0;
char * path_argv[2] = {path, NULL};
FTS *fts = fts_open(path_argv, FTS_PHYSICAL, NULL);
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
cpath := C.CString("/usr")
defer C.free(unsafe.Pointer(cpath))
fmt.Println(C.get_total_size(cpath))
}
Note that your program has one bug and one possible problem:
A bug is that the call C.CString allocates a chunk of memory by performing a call to malloc(3) from the linked C library, and you did not free that memory block.
The symbol NULL is defined in "stddef.h"; you might or might not get an error when compiling.
I've fixed both problems in my example.
A further improvement over our example might be leveraging the ability of fts_* functions to scan multiple paths in a single run; if we were to implement that, it would have more sense to allocate the array for the 1st argument of fts_open on the Go's side:
package main
/*
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char * const *path_argv)
{
uintmax_t total_size = 0;
FTS *fts = fts_open(path_argv, FTS_PHYSICAL, NULL);
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println(getTotalSize("/usr", "/etc"))
}
func getTotalSize(paths ...string) uint64 {
argv := make([]*C.char, len(paths)+1)
for i, path := range paths {
argv[i] = C.CString(path)
defer C.free(unsafe.Pointer(argv[i]))
}
return uint64(C.get_total_size(&argv[0]))
}
Note that here we did not explicitly zero out the last argument of argv because — contrary to C, — Go initializes each allocated memory block with zeroes, so once argv is allocated, all its memory is already zeroed.
you are getting the error cause "fts_open" requires a character pointer to an array which is NULL terminating like char *argv[] = { path, NULL };..(https://linux.die.net/man/3/fts_open)
package main
/*
#include <stdint.h>
#include <fts.h>
#include <sys/stat.h>
uintmax_t get_total_size(char *path)
{
uintmax_t total_size = 0;
char *argv[] = { path, NULL };
FTS *fts = fts_open(argv, FTS_PHYSICAL, NULL);
if (fts == NULL)
return 0;
FTSENT *fent;
while ((fent = fts_read(fts)) != NULL)
if (fent->fts_info == FTS_F)
total_size += fent->fts_statp->st_size;
fts_close(fts);
return total_size;
}
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.get_total_size(C.CString("/usr")))
}
so adding the array pointer will fix the code.
The same code works when compiled with GCC but fts_open returns NULL.I am guessing that there is some difference in optimization between gcc and cgo(not very sure)
I tried some test results and was able to find that when compiling with GCC the char **pointer is getting NULL-terminated but in the case of cgo it was not getting NULL-terminated so you were getting "SIGSEGV" as your code is reading invalid memory reference
#include <stdio.h>
#include <string.h>
void try(char **p)
{
while (*p != NULL)
{
printf("%zu\n", strlen(*p));
++p;
}
}
void get_total_size(char *path)
{
try(&path);
}
int main()
{
get_total_size("/usr");
}
c code (which works)
package main
/*
#include <stdio.h>
#include <string.h>
void try(char **p)
{
while (*p != NULL)
{
printf("%zu\n", strlen(*p));
++p;
}
}
void get_total_size(char *path)
{
try(&path);
}
*/
import "C"
func main() {
C.get_total_size(C.CString("/usr"))
}
same go code you will face error

How to return go (array/slice/ist) to a C function

I have C code in which I am calling golang functions. I am able to do it for primitives data types (int/float etc.) but I want to return some other data structure like array/list/slice.
I could not find any solution on internet.
Looking for help.
Want to return a array/slice/list of string data type.
It would be helpful if you provide additional information, i.e. example code you are currently working on.
As stated from the Cgo documentation page:
Go array types are not supported; use a C pointer
To do so
hello.go
package main
// #include <stdlib.h>
import "C"
import "unsafe"
// StringSlice is a wrapper arround GoStringSlice to make it usable in C.
//export StringSlice
func StringSlice() **C.char {
x := GoStringSlice()
ret := C.malloc(C.size_t(len(x)) * C.size_t(unsafe.Sizeof(uintptr(0))))
// convert to usable format so we are able to fill it with data
pRet := (*[1<<30 - 1]*C.char)(ret)
for i, item := range x {
pRet[i] = C.CString(item)
}
return (**C.char)(ret)
}
func GoStringSlice() []string {
return []string{
"Hello",
"World",
}
}
func main() {}
hello.c
#include <stdio.h>
#include "hello.h"
int main() {
printf("Hello from C!\n");
char **slice = StringSlice();
int numItems = sizeof(slice) / sizeof(char *);
printf("Number of items: %d\n", numItems+1);
printf("String #0: %s\n", *slice);
slice++;
printf("String #1: %s\n", *slice);
return 0;
}
You have to execute go build -buildmode=c-archive hello.go which will generate a hello.h and hello.a.
The hello.a has to be compiled with your C code: gcc -pthread hello.c hello.a -o hello.

C references passed to Go can not recognize typedef void*?

c.h
typedef void* MVar;
C_FUNC(
MVar* myvar //[out], return value
)
test.go
var cvar unsafe.Pointer
_ = C.C_FUNC(&cvar)
when I run test.go, it tells me
cannot use _cgo5 (type *unsafe.Pointer) as type *_Ctype_MVar
in argument to _Cfunc_C_FUNC
In this document Command cgo: Go references to C, it says "The C type void* is represented by Go's unsafe.Pointer."
cannot use _cgo5 (type *unsafe.Pointer) as type *_Ctype_MVar
in argument to _Cfunc_C_FUNC
The Go toolchain says you have a type mismatch.
An equivalent, working example, with matching types,
package main
import (
"fmt"
"unsafe"
)
/*
typedef void* pv_t;
void cfunc(pv_t *p);
#include <stdio.h>
int i;
void cfunc(pv_t *p) {
i = 42;
*p = &i;
printf("%p %p %d\n", p, *p, *(int*)(*p));
}
*/
import "C"
func main() {
var cptr unsafe.Pointer
C.cfunc((*C.pv_t)(&cptr))
fmt.Println(&cptr, cptr, *(*C.int)(cptr))
}
Output:
$ go run so.go
0xc000010028 0x592d18 42
0xc000010028 0x592d18 42
$

Compiling CGO files in Golang package

I am trying to use CGO to bundle C files with a Golang package. Following instructions here:
https://karthikkaranth.me/blog/calling-c-code-from-go/
http://akrennmair.github.io/golang-cgo-slides/#1
https://golang.org/cmd/cgo/
I am getting this error:
# main
src/main/main.go:16:8: could not determine kind of name for C.free
src/main/main.go:23:10: could not determine kind of name for C.greet
here is the structure:
main.go just looks like:
package main
// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include "genericc/greeter.h"
import "C"
import (
"fmt"
"unsafe"
)
func main() {
name := C.CString("Gopher")
defer C.free(unsafe.Pointer(name))
year := C.int(2018)
ptr := C.malloc(C.sizeof_char * 1024)
defer C.free(unsafe.Pointer(ptr))
size := C.greet(name, year, (*C.char)(ptr))
b := C.GoBytes(ptr, size)
fmt.Println(string(b))
}
and I run test.sh to build it:
#!/usr/bin/env bash
dir="$(cd `dirname "$0"` && pwd)"
export GOPATH="$dir"
cd "$dir"
export CGOFILES=main
go install main
but when I run the bash script I get that error.
I follow the instructions:
Command cgo
If the import of "C" is immediately preceded by a comment, that
comment, called the preamble, is used as a header when compiling the C
parts of the package. For example:
// #include <stdio.h>
// #include <errno.h>
import "C"
or
/*
#include <stdio.h>
#include <errno.h>
*/
import "C"
For example,
gocbuf.go:
package main
import (
"fmt"
"unsafe"
)
/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int printData(unsigned char *data) {
return printf("cData: %lu \"%s\"\n", (long unsigned int)strlen(data), data);
}
*/
import "C"
func main() {
// Allocate C data buffer.
width, height := 8, 2
lenData := width * height
// add string terminating null byte
cData := (*C.uchar)(C.calloc(C.size_t(lenData+1), C.sizeof_uchar))
// When no longer in use, free C allocations.
defer C.free(unsafe.Pointer(cData))
// Go slice reference to C data buffer,
// minus string terminating null byte
gData := (*[1 << 30]byte)(unsafe.Pointer(cData))[:lenData:lenData]
// Write and read cData via gData.
for i := range gData {
gData[i] = '.'
}
copy(gData[0:], "Data")
gData[len(gData)-1] = 'X'
fmt.Printf("gData: %d %q\n", len(gData), gData)
C.printData(cData)
}
Output:
$ go run gocbuf.go
gData: 16 "Data...........X"
cData: 16 "Data...........X"
$
Your code organization makes no sense to me.
You should have package greeter, which wraps C functions via cgo. For example,
src
└── greeter
├── greeter.c
├── greeter.go
└── greeter.h
with skeleton files
greeter.go:
package greeter
/*
#include "greeter.h"
*/
import "C"
greeter.c:
#include "greeter.h"
greeter.h
/* C header file */
To install the greeter package, simply use go install.
Don't use relative paths. Don't use bash scripts.
Thanks to #peterSO, this is what worked:
package main
// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include "../genericc/greeter.h"
// #include "../genericc/greeter.c" // ! no whitespace after this line
import "C"
import (
"fmt"
"unsafe"
)
func main() {
name := C.CString("Gopher")
defer C.free(unsafe.Pointer(name))
year := C.int(2018)
ptr := C.malloc(C.sizeof_char * 1024)
defer C.free(unsafe.Pointer(ptr))
size := C.greet(name, year, (*C.char)(ptr))
b := C.GoBytes(ptr, size)
fmt.Println(string(b))
}

unexpected type: ... with cgo in Go

I'm new to Go and trying to learn how to call C from Go. I wrote this program to open a named semaphore, get the value and print it to the screen.
When I run it go build semvalue.go I get the error:
./semvalue.go:16:14: unexpected type: ...
What does this mean? What am I doing wrong?
package main
import "fmt"
// #cgo LDFLAGS: -pthread
// #include <stdlib.h>
// #include <fcntl.h>
// #include <sys/stat.h>
// #include <semaphore.h>
import "C"
func main() {
name := C.CString("/fram")
defer C.free(name)
fram_sem := C.sem_open(name, C.O_CREAT, C.mode_t(0644), C.uint(1))
var val int
ret := C.sem_getvalue(fram_sem, val)
fmt.Println(val)
C.sem_close(fram_sem)
}
Thank you.
The message is confusing, until you realize that the ... is the variadic portion of a C function. You can't use C variadic functions directly from Go, so you'll have to write a small wrapper in C to call sem_open.
A couple more notes:
C.free should be called with C.free(unsafe.Pointer(name))
val needs to be a *C.int
sem_getvalue uses errno, so you should call it with ret, err := C.sem_getvalue...

Resources