在Golang中是否有用于C FILE的数据类型?

huangapple go评论107阅读模式
英文:

Is there a data type for c FILE in Golang?

问题

我最近遇到了一个问题,尝试将https://github.com/robpike/strings编译为DLL(共享库),其中cgo不识别os.File作为可导出的数据类型。我想将这个dll导入到一个使用ctypes的Python脚本中。

我对Go语言相对陌生,在大量搜索后,我找不到我的问题的答案。

strings.go

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"C"
)

var (
	min    = flag.Int("min", 6, "minimum length of UTF-8 strings printed, in runes")
	max    = flag.Int("max", 256, "maximum length of UTF-8 strings printed, in runes")
	ascii  = flag.Bool("ascii", false, "restrict strings to ASCII")
	offset = flag.Bool("offset", false, "show file name and offset of start of each string")
)

var stdout *bufio.Writer

func main() {
}

//export do
func do(file *os.File) {
	in := bufio.NewReader(file)
	str := make([]rune, 0, *max)
	filePos := int64(0)
	print := func() {
		if len(str) >= *min {
			s := string(str)
			// if *offset {
				// fmt.Printf("%s:#%d:\t%s\n", name, filePos-int64(len(s)), s)
			// } else {
			fmt.Println(s)
			// }
		}
		str = str[0:0]
	}
	for {
		var (
			r   rune
			wid int
			err error
		)
		// One string per loop.
		for ; ; filePos += int64(wid) {
			r, wid, err = in.ReadRune()
			if err != nil {
				if err != io.EOF {
					log.Print(err)
				}
				return
			}
			if !strconv.IsPrint(r) || *ascii && r >= 0xFF {
				print()
				continue
			}
			// It's printable. Keep it.
			if len(str) >= *max {
				print()
			}
			str = append(str, r)
		}
	}
}

期望结果go build -o strings.dll -buildmode=c-shared 输出具有导出函数dostrings.dll

问题.\strings.go:41:15: Go type not supported in export: os.File

编辑:我遇到了这个问题:read file descriptor: The handle is invalid.

Python驱动程序(在Windows amd64上使用Python 3.10.5):

from ctypes import *
mydll = cdll.LoadLibrary("./strings.dll")

strings = mydll.dodo
strings.argtypes = [c_int]
strings.restype = c_void_p

with open("./go.mod", "rb") as h:
    print(strings(h.fileno()))
英文:

I recently ran into an issue trying to compile https://github.com/robpike/strings as a DLL (shared lib), where cgo doesn't recognise os.File as an exportable data type. I want to import this dll into a python script with ctypes.

I am relatively new to golang, and after a lot of googling I couldn't find an answer to my question.

strings.go

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"C"
)

var (
	min    = flag.Int("min", 6, "minimum length of UTF-8 strings printed, in runes")
	max    = flag.Int("max", 256, "maximum length of UTF-8 strings printed, in runes")
	ascii  = flag.Bool("ascii", false, "restrict strings to ASCII")
	offset = flag.Bool("offset", false, "show file name and offset of start of each string")
)

var stdout *bufio.Writer

func main() {
}

//export do
func do(file *os.File) {
	in := bufio.NewReader(file)
	str := make([]rune, 0, *max)
	filePos := int64(0)
	print := func() {
		if len(str) >= *min {
			s := string(str)
			// if *offset {
				// fmt.Printf("%s:#%d:\t%s\n", name, filePos-int64(len(s)), s)
			// } else {
			fmt.Println(s)
			// }
		}
		str = str[0:0]
	}
	for {
		var (
			r   rune
			wid int
			err error
		)
		// One string per loop.
		for ; ; filePos += int64(wid) {
			r, wid, err = in.ReadRune()
			if err != nil {
				if err != io.EOF {
					log.Print(err)
				}
				return
			}
			if !strconv.IsPrint(r) || *ascii && r >= 0xFF {
				print()
				continue
			}
			// It's printable. Keep it.
			if len(str) >= *max {
				print()
			}
			str = append(str, r)
		}
	}
}

Expectation: go build -o strings.dll -buildmode=c-shared outputs strings.dll with exported function do

Problem: .\strings.go:41:15: Go type not supported in export: os.File

EDIT:I am running into this issue: read file descriptor: The handle is invalid.

Python driver (3.10.5 on windows amd64):

from ctypes import *
mydll = cdll.LoadLibrary("./strings.dll")

strings = mydll.dodo
strings.argtypes = [c_int]
strings.restype = c_void_p

with open("./go.mod", "rb") as h:
	print(strings(h.fileno()))

答案1

得分: 4

Cgo不支持将结构映射到C语言。实际上,在导出的函数中,你只能使用字符串、整数和本地C类型。

调用函数可以传递文件描述符而不是os.File。文件描述符是一个整数。

我在你的代码中添加了func dodo(fd int)函数,并导出该函数代替do函数。

package main

import (
	"C"
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
)

var (
	min    = flag.Int("min", 6, "minimum length of UTF-8 strings printed, in runes")
	max    = flag.Int("max", 256, "maximum length of UTF-8 strings printed, in runes")
	ascii  = flag.Bool("ascii", false, "restrict strings to ASCII")
	offset = flag.Bool("offset", false, "show file name and offset of start of each string")
)

var stdout *bufio.Writer

func main() {
}

//export dodo
func dodo(fd int) {
	file := os.NewFile(uintptr(fd), "file descriptor")
	if file == nil {
		return
	}
	do(file)
}

func do(file *os.File) {
	in := bufio.NewReader(file)
	str := make([]rune, 0, *max)
	filePos := int64(0)
	print := func() {
		if len(str) >= *min {
			s := string(str)
			// if *offset {
			// fmt.Printf("%s:#%d:\t%s\n", name, filePos-int64(len(s)), s)
			// } else {
			fmt.Println(s)
			// }
		}
		str = str[0:0]
	}
	for {
		var (
			r   rune
			wid int
			err error
		)
		// One string per loop.
		for ; ; filePos += int64(wid) {
			r, wid, err = in.ReadRune()
			if err != nil {
				if err != io.EOF {
					log.Print(err)
				}
				return
			}
			if !strconv.IsPrint(r) || *ascii && r >= 0xFF {
				print()
				continue
			}
			// It's printable. Keep it.
			if len(str) >= *max {
				print()
			}
			str = append(str, r)
		}
	}
}

构建代码(Linux):

go build -o libstrings.so -buildmode=c-shared .

这个命令会生成libstrings.hlibstrings.so文件。

下面是一个使用libstrings.so的示例程序some.c

// perror
#include <stdio.h>
// open and O_RDONLY
#include <fcntl.h>
// close
#include <unistd.h>
// dodo
#include "libstrings.h"

int main(int argc, char **argv) {
    int fd = open("go.mod", O_RDONLY);
    if (fd < 0) {
        perror("Failed to open file");
        return 1;
    }
    dodo(fd);
    close(fd);
    return 0;
}

构建:

gcc -o some.exe some.c -L ./ -lstrings

执行:

LD_LIBRARY_PATH=. ./some.exe 

结果:它按预期打印了go.mod文件的内容。

module example.org
go 1.19
英文:

Cgo doesn't support mapping structures to C. Actually, all you can use in the exported functions is strings, integers and native C types.

Instead of os.File the calling function can pass file decriptor. It is an integer.

I added func dodo(fd int) to your code and export this function instead of do.

package main
import (
&quot;C&quot;
&quot;bufio&quot;
&quot;flag&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
&quot;os&quot;
&quot;strconv&quot;
)
var (
min    = flag.Int(&quot;min&quot;, 6, &quot;minimum length of UTF-8 strings printed, in runes&quot;)
max    = flag.Int(&quot;max&quot;, 256, &quot;maximum length of UTF-8 strings printed, in runes&quot;)
ascii  = flag.Bool(&quot;ascii&quot;, false, &quot;restrict strings to ASCII&quot;)
offset = flag.Bool(&quot;offset&quot;, false, &quot;show file name and offset of start of each string&quot;)
)
var stdout *bufio.Writer
func main() {
}
//export dodo
func dodo(fd int) {
file := os.NewFile(uintptr(fd), &quot;file descriptor&quot;)
if file == nil {
return
}
do(file)
}
func do(file *os.File) {
in := bufio.NewReader(file)
str := make([]rune, 0, *max)
filePos := int64(0)
print := func() {
if len(str) &gt;= *min {
s := string(str)
// if *offset {
// fmt.Printf(&quot;%s:#%d:\t%s\n&quot;, name, filePos-int64(len(s)), s)
// } else {
fmt.Println(s)
// }
}
str = str[0:0]
}
for {
var (
r   rune
wid int
err error
)
// One string per loop.
for ; ; filePos += int64(wid) {
r, wid, err = in.ReadRune()
if err != nil {
if err != io.EOF {
log.Print(err)
}
return
}
if !strconv.IsPrint(r) || *ascii &amp;&amp; r &gt;= 0xFF {
print()
continue
}
// It&#39;s printable. Keep it.
if len(str) &gt;= *max {
print()
}
str = append(str, r)
}
}
}

Build the code (Linux):

go build -o libstrings.so -buildmode=c-shared .

This command produced libstrings.h and libstrings.so

Example some.c program that uses libstrings.so

// perror
#include &lt;stdio.h&gt;
// open and O_RDONLY
#include&lt;fcntl.h&gt;
// close
#include &lt;unistd.h&gt;
// dodo
#include &quot;libstrings.h&quot;
int main(int argc, char **argv) {
int fd = open(&quot;go.mod&quot;, O_RDONLY);
if (fd &lt; 0) {
perror(&quot;Failed to open file&quot;);
return 1;
}
dodo(fd);
close(fd);
return 0;
}

Build:

gcc -o some.exe some.c -L ./ -lstrings

Execute:

LD_LIBRARY_PATH=. ./some.exe 

Result: it prints the contents of go.mod file as expected.

module example.org
go 1.19

huangapple
  • 本文由 发表于 2022年10月23日 13:38:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/74168903.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定