CGO - convert a c.char array to string

2,276 views
Skip to first unread message

nicolas...@yahoo.fr

unread,
Aug 5, 2018, 6:05:42 AM8/5/18
to golang-nuts
Hello

i have a little problem to convert a C char array to a string array in golang.


func RVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv **C.char, argc C.size_t) {
    temp := fmt.Sprintf("Hello %s %d %s!", C.GoString(input), argc,  C.GoString(*argv)

    // return to C program, the string
    result := C.CString(temp)
    defer C
.free(unsafe.Pointer(result))
   
var size = C.strlen(result) + 1
   
if size > outputsize {
        size
= outputsize
   
}
    C
.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
}

I did this code only to print the content of argv for test purpose. This code only return the first string of argv array.

Do you know a standard method to iterate & convert all the argv array in one golang string array  ?

Jan Mercl

unread,
Aug 5, 2018, 6:16:13 AM8/5/18
to nicolas...@yahoo.fr, golang-nuts
On Sun, Aug 5, 2018 at 12:05 PM nicolas_boiteux via golang-nuts <golan...@googlegroups.com> wrote:

> Do you know a standard method to iterate & convert all the argv array in one golang string array ?

Something like (not tested):

var a []string
for p := argv; *p != nil; p = (**C.char)(unsafe.Pointer(uintptr(p)+unsafe.Sizeof(*p))) {
        a = append(a, C.GoSting(*p))
}

--

-j

nicolas...@yahoo.fr

unread,
Aug 5, 2018, 6:29:54 AM8/5/18
to golang-nuts
it returns :

.\armago_x64.go:19: cannot convert p (type **_Ctype_char) to type uintptr

I am a little lost with this conversion, I do not understand how the iterator should work. I hope there was a simple command like GoString to do it.

Jan Mercl

unread,
Aug 5, 2018, 6:48:20 AM8/5/18
to nicolas...@yahoo.fr, golang-nuts
On Sun, Aug 5, 2018 at 12:29 PM nicolas_boiteux via golang-nuts <golan...@googlegroups.com> wrote:

> it returns :
>
> .\armago_x64.go:19: cannot convert p (type **_Ctype_char) to type uintptr

Ok, this time bit more tested

jnml@4670:~/tmp> cat main.c
#include <stdio.h>

int main(int argc, char **argv) {
char **p;
for (p = argv; *p != 0; p++) {
printf("%s\n", *p);
}
}
jnml@4670:~/tmp> ccgo main.c && ./a.out abc def ghi
./a.out
abc
def
ghi
jnml@4670:~/tmp> ccgo -o main.go main.c
jnml@4670:~/tmp> cat main.go
// Code generated by 'ccgo -o main.go main.c', DO NOT EDIT.

// +build linux,amd64

package main

import (
"math"
"os"
"unsafe"

)

const (
null = uintptr(0)
)

var (
_ = math.Pi
_ = os.DevNull
_ = unsafe.Pointer(null)

nz32 float32
nz64 float64
)

func init() { nz32 = -nz32 }
func init() { nz64 = -nz64 }

func alloca(p *[]uintptr, n int) uintptr   { r := crt.MustMalloc(n); *p = append(*p, r); return r }
func preinc(p *uintptr, n uintptr) uintptr { *p += n; return *p }

func main() {
psz := unsafe.Sizeof(uintptr(0))
argv := crt.MustCalloc((len(os.Args) + 1) * int(psz))
p := argv
for _, v := range os.Args {
*(*uintptr)(unsafe.Pointer(p)) = crt.CString(v)
p += psz
}
a := os.Environ()
env := crt.MustCalloc((len(a) + 1) * int(psz))
p = env
for _, v := range a {
*(*uintptr)(unsafe.Pointer(p)) = crt.CString(v)
p += psz
}
*(*uintptr)(unsafe.Pointer(Xenviron)) = env
X_start(crt.NewTLS(), int32(len(os.Args)), argv)
}

// linking main.o

// X__ccgo_va_end *void, escapes: true, builtin.h:36:6
var X__ccgo_va_end = bss + 0

// X__ccgo_va_start *void, escapes: true, builtin.h:37:6
var X__ccgo_va_start = bss + 8

// Xmain is defined at main.c:3:5
func Xmain(tls crt.TLS, _argc int32, _argv uintptr /* **int8 */) (r int32) {
var _p uintptr // **int8

_p = _argv
_1:
if (*(*uintptr)(unsafe.Pointer(_p))) == 0 {
goto _3
}

crt.Xprintf(tls, ts+0 /* "%s\n" */, *(*uintptr)(unsafe.Pointer(_p)))
_p += 8
goto _1

_3:
return r
}

// linking crt0.o

// X__stdfiles [3]uintptr, escapes: true, crt0.c:3:15
var X__stdfiles = bss + 16

// Xenviron **int8, escapes: true, crt0.c:4:6
var Xenviron = bss + 40

// Xstdin *void, escapes: true, crt0.c:5:6
var Xstdin = bss + 48 // pointer to void

func init() { *(*uintptr)(unsafe.Pointer(Xstdin)) = X__stdfiles }

// Xstdout *void, escapes: true, crt0.c:5:31
var Xstdout = bss + 56 // pointer to void

func init() { *(*uintptr)(unsafe.Pointer(Xstdout)) = X__stdfiles + 8 }

// Xstderr *void, escapes: true, crt0.c:5:57
var Xstderr = bss + 64 // pointer to void

func init() { *(*uintptr)(unsafe.Pointer(Xstderr)) = X__stdfiles + 16 }

// X_start is defined at crt0.c:7:6
func X_start(tls crt.TLS, _argc int32, _argv uintptr /* **int8 */) {
crt.X__register_stdfiles(tls, *(*uintptr)(unsafe.Pointer(Xstdin)), *(*uintptr)(unsafe.Pointer(Xstdout)), *(*uintptr)(unsafe.Pointer(Xstderr)), Xenviron)
crt.X__builtin_exit(tls, Xmain(tls, _argc, _argv))
}

func init() { *(*uintptr)(unsafe.Pointer(Xstdin)) = X__stdfiles }

func init() { *(*uintptr)(unsafe.Pointer(Xstdout)) = X__stdfiles + 8 }

func init() { *(*uintptr)(unsafe.Pointer(Xstderr)) = X__stdfiles + 16 }

var (
bss     = crt.BSS(&bssInit[0])
bssInit [72]byte
ts      = crt.TS("%s\n\x00")
)
jnml@4670:~/tmp> 

I hope you can extract the code for your case from the above mechanically produced Go code. Note that ccgo uses untrptrs instead of normal pointers but that should be an easy adjustment, I hope.

--

-j

nicolas...@yahoo.fr

unread,
Aug 5, 2018, 8:50:39 AM8/5/18
to golang-nuts
not sure to understand cause the iteration in your example is done on os interface from golang not from c char array :(

Miki Tebeka

unread,
Aug 5, 2018, 8:59:13 AM8/5/18
to golang-nuts
Hi,
 
i have a little problem to convert a C char array to a string array in golang
Maybe something like

var offset = unsafe.Sizeof(uintptr(0))

func argv2go
(argv **C.char) []string {
   
var out []string
   
for *argv != nil {
       
out = append(out, C.GoString(*argv))
        argv
= (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + offset))
   
}

   
return out
}


 

Jan Mercl

unread,
Aug 5, 2018, 9:02:47 AM8/5/18
to nicolas...@yahoo.fr, golang-nuts
On Sun, Aug 5, 2018 at 2:50 PM nicolas_boiteux via golang-nuts <golan...@googlegroups.com> wrote:

> not sure to understand cause the iteration in your example is done on os interface from golang not from c char array :(

Not sure what you mean by 'os interface'. Here's the part that iterates **C.char, except pointers are transformed to uintptrs. Other that that it's what you're, I think, after

// Xmain is defined at main.c:3:5
func Xmain(tls crt.TLS, _argc int32, _argv uintptr /* **int8 */) (r int32) {
var _p uintptr // **int8

_p = _argv
_1:
if (*(*uintptr)(unsafe.Pointer(_p))) == 0 {
goto _3
}

crt.Xprintf(tls, ts+0 /* "%s\n" */, *(*uintptr)(unsafe.Pointer(_p)))
_p += 8
goto _1

_3:
return r
}

Manually converting (untested again):

func foo(argv **C.char) []string {
        var a []string
        for p := argv; *p != nil; *(*uintptr)(unsafe.Pointer(p)) += unsafe.Sizeof(*p) {
                a = append(a, GoString(*p))
        }
        rerurn a
}


--

-j

nicolas...@yahoo.fr

unread,
Aug 5, 2018, 9:47:00 AM8/5/18
to golang-nuts
Miki & Jan thanks

i tested the both methods. Dll compiles normaly, but program crash.

With Miki code the first time, i run the code, i saw that string array contains also other characters that not come from argv.

nicolas...@yahoo.fr

unread,
Aug 5, 2018, 11:17:05 AM8/5/18
to golang-nuts
i found another thing.

func RVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv **C.char, argc C.int) {
   
var offset = unsafe.Sizeof(uintptr(0))
   
var out []string
    limit := *(*int)(unsafe.Pointer(&argc))
   
if limit < 1000 {
       
for index := 0; index < limit; index++ {

           
out = append(out, C.GoString(*argv))
            argv
= (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + offset))
       
}
   
}

    temp
:= fmt.Sprintf("Hello %s %d %s!", C.GoString(input), limit,  out)

   
// Return a result to Arma

    result
:= C.CString(temp)
    defer C
.free(unsafe.Pointer(result))
   
var size = C.strlen(result) + 1
   
if size > outputsize {
        size
= outputsize
   
}
    C
.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
}Saisissez le code ici...
it seems there is also a bug in the main program cause the argc variable, the first execution time contains a memory adress instead of int

nicolas...@yahoo.fr

unread,
Aug 6, 2018, 1:29:33 AM8/6/18
to golang-nuts
thanks you, the A3 community finaly help me to solve the last problem

    for index := C.int(0); index < argc; index++ {

       
out = append(out, C.GoString(*argv))
        argv
= (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + offset))
   
}


I dont really understand why but it seems there was a problem with my casting method with unsafe

*(*int)(unsafe.Pointer(limit)

Ian Lance Taylor

unread,
Aug 6, 2018, 1:53:07 PM8/6/18
to nicolas...@yahoo.fr, golang-nuts
On Sun, Aug 5, 2018 at 10:29 PM, nicolas_boiteux via golang-nuts
<golan...@googlegroups.com> wrote:
>
> thanks you, the A3 community finaly help me to solve the last problem
>
> for index := C.int(0); index < argc; index++ {
> out = append(out, C.GoString(*argv))
> argv = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) +
> offset))
> }
>
> I dont really understand why but it seems there was a problem with my
> casting method with unsafe
>
> *(*int)(unsafe.Pointer(limit)

See https://golang.org/pkg/unsafe for the exact ways that you are
permitted to use unsafe.Pointer.

Ian
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

nicolas...@yahoo.fr

unread,
Aug 7, 2018, 2:35:07 AM8/7/18
to golang-nuts
yes i used the code from this page:

func Float64bits(f float64) uint64 {
	return *(*uint64)(unsafe.Pointer(&f))
}

adapted to my use case

*(*int)(unsafe.Pointer(limit)

Ian Lance Taylor

unread,
Aug 7, 2018, 9:57:16 AM8/7/18
to nicolas...@yahoo.fr, golang-nuts
On Mon, Aug 6, 2018 at 11:34 PM, nicolas_boiteux via golang-nuts
<golan...@googlegroups.com> wrote:
>
> yes i used the code from this page:
>
> func Float64bits(f float64) uint64 {
> return *(*uint64)(unsafe.Pointer(&f))
> }
>
> adapted to my use case
>
> *(*int)(unsafe.Pointer(limit)

As the docs say, that is only permissible when T2 is no larger than T1
and the two share an equivalent memory layout. In your case T2 is the
Go type `int` and T1 is the C type `int`. On 64-bit systems the C
type `int` is 32 bits and the Go type `int` is 64 bits. So T2 is
larger than T1 and your conversion is not permitted by the unsafe
rules.

Ian
Reply all
Reply to author
Forward
0 new messages