2016-02-27 19 views
5

Tôi có một byte.Buffer mà tôi đóng gói với dữ liệu bằng cách sử dụng hàm binary.Write(). Sau đó tôi cần gửi mảng byte này đến một hàm C. Sử dụng Go 1.6 Tôi đã không thành công trong việc tìm ra điều này.Chuyển đổi Go [] byte sang C * char

buf := new(bytes.Buffer) //create my buffer 
.... 
binary.Write(buf, binary.LittleEndian, data) //write my data to buffer here 
addr := (*C.uchar)(unsafe.Pointer(&buf.Bytes()[0])) //convert buffers byte array to a C array 
rc := C.the_function(addr, C.int(buf.Len())) //Fails here 

Nó thất bại trên dòng gọi hàm C nói:

panic: runtime error: cgo argument has Go pointer to Go pointer 

Chức năng C:

int the_function(const void *data, int nbytes); 

tôi đã có thể nhận được như sau để làm việc, nhưng nó cảm thấy sai chuyển đổi mảng byte thành chuỗi. Có cách nào tốt hơn để làm điều này? Phương pháp này có gây rủi ro tác dụng phụ cho dữ liệu không?

addr := unsafe.Pointer(C.CString(string(buf.Bytes()[0])) 

Một lần nữa, điều này cần phải làm việc trong Go 1.6, giới thiệu quy tắc con trỏ cgo nghiêm ngặt hơn.

Cảm ơn bạn.

Trả lời

7

Nếu bạn muốn sử dụng cách tiếp cận đầu tiên, bạn cần tạo slice bên ngoài đối số cuộc gọi hàm và tránh tiêu đề lát được phân bổ tạm thời hoặc cấu trúc bên ngoài trong đối số, vì vậy kiểm tra cgo không nhìn thấy nó một con trỏ được lưu trữ trong Go.

b := buf.Bytes() 
rc := C.the_function(unsafe.Pointer(&b[0]), C.int(buf.Len())) 

Phương pháp C.CString sẽ được an toàn hơn, trong đó các dữ liệu được sao chép vào một bộ đệm C, vì vậy không có con trỏ to Go bộ nhớ, và không có cơ hội slice đằng sau bytes.Buffer sẽ được sửa đổi hoặc đi ra khỏi phạm vi. Bạn sẽ muốn chuyển đổi toàn bộ chuỗi, không chỉ là byte đầu tiên. Phương pháp này không cần phải phân bổ và sao chép hai lần, tuy nhiên nếu lượng dữ liệu nhỏ, nó có lẽ không phải là một mối quan tâm so với chi phí của bản thân cuộc gọi cgo.

str := buf.String() 
p := unsafe.Pointer(C.CString(str)) 
defer C.free(p) 
rc = C.the_function(p, C.int(len(str))) 

Nếu 2 bản sao của dữ liệu là không thể chấp nhận được trong dung dịch đó, có một lựa chọn thứ ba, nơi bạn malloc C đệm cho mình, và tạo một bản sao duy nhất vào đệm rằng:

p := C.malloc(C.size_t(len(b))) 
defer C.free(p) 

// copy the data into the buffer, by converting it to a Go array 
cBuf := (*[1 << 30]byte)(p) 
copy(cBuf[:], b) 
rc = C.the_function(p, C.int(buf.Len())) 

Nhưng với cả hai tùy chọn thứ hai, đừng quên giải phóng con trỏ malloc'ed.

+0

chuyển [] byte thành chuỗi c có thể không phải là một ý kiến ​​hay. Bởi vì '\ 0' trong byte [] sẽ kết thúc chuỗi c, và độ dài của chuỗi c có thể không bằng chiều dài của byte [] gốc. –

+0

@ bronzeman: rõ ràng, nhưng hàm trong câu hỏi có độ dài bộ đệm làm đối số và không mong đợi một chuỗi bị hủy. 'C.CString' thêm byte null nếu nó cần thiết, nhưng chúng ta bỏ qua nó bằng cách truyền độ dài chuỗi chính xác. – JimB

0

Chương trình của bạn bị treo do quy tắc chuyển con trỏ vào C thay đổi trong go1.6 (xem https://tip.golang.org/doc/go1.6#cgo để biết chi tiết).

Tôi không biết tại sao chương trình của bạn gặp sự cố, vì vậy tôi đã tạo ra vấn đề Go https://github.com/golang/go/issues/14546.

Nhưng bất kể câu trả lời nào về vấn đề này, tôi sẽ không sử dụng bit nội bộ byte.Buffer (như bạn làm) để chuyển trực tiếp vào cgo. Việc thực hiện byte.Buffer có thể thay đổi trong tương lai và chương trình của bạn sẽ bắt đầu phá vỡ một cách bí ẩn. Tôi sẽ chỉ sao chép dữ liệu bạn cần vào bất kỳ cấu trúc nào là thích hợp và sử dụng nó để chuyển vào cgo.

Alex

+0

OP không sử dụng bất kỳ bit nội bộ nào của byte.Buffer. Tôi đã giải thích lý do, cũng như sự lừa đảo của vấn đề của bạn. Không có gì sai khi sử dụng slice được trả về bởi bộ đệm – JimB