mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 12:32:21 +00:00
ch2-05: 完成C++类到Go对象包装部分
This commit is contained in:
parent
6b61c309ce
commit
c6bc5a45a3
@ -11,13 +11,14 @@ CGO是C语言和Go语言之间的桥梁,原则上无法直接支持C++的类
|
|||||||
为了演示简单,我们基于`std::string`做一个最简单的缓存对象MyBuffer。除了构造函数和析构函数之外,只有两个成员函数分别是返回底层的数据指针和缓存的大小。因为是二进制缓存,我们可以在里面中放置任意数据。
|
为了演示简单,我们基于`std::string`做一个最简单的缓存对象MyBuffer。除了构造函数和析构函数之外,只有两个成员函数分别是返回底层的数据指针和缓存的大小。因为是二进制缓存,我们可以在里面中放置任意数据。
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
|
// my_buffer.h
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class MyBuffer {
|
struct MyBuffer {
|
||||||
std::string* s_;
|
std::string* s_;
|
||||||
|
|
||||||
MyBuffer(int size) {
|
MyBuffer(int size) {
|
||||||
this->s_ = new std::string(n, char('\0'));
|
this->s_ = new std::string(size, char('\0'));
|
||||||
}
|
}
|
||||||
~MyBuffer() {
|
~MyBuffer() {
|
||||||
delete this->s_;
|
delete this->s_;
|
||||||
@ -26,8 +27,8 @@ class MyBuffer {
|
|||||||
int Size() const {
|
int Size() const {
|
||||||
return this->s_->size();
|
return this->s_->size();
|
||||||
}
|
}
|
||||||
const char* Data() {
|
char* Data() {
|
||||||
return this->s_->data();
|
return (char*)this->s_->data();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -80,17 +81,22 @@ int MyBuffer_Size(MyBuffer_T* p);
|
|||||||
然后就可以基于C++的MyBuffer类定义这些C语言包装函数。我们创建对应的`my_buffer_capi.cc`文件如下:
|
然后就可以基于C++的MyBuffer类定义这些C语言包装函数。我们创建对应的`my_buffer_capi.cc`文件如下:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
// my_buffer_capi.h
|
// my_buffer_capi.cc
|
||||||
|
|
||||||
|
#include "./my_buffer.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#incude "./my_buffer_capi.h"
|
#include "./my_buffer_capi.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyBuffer_T: MyBuffer {};
|
struct MyBuffer_T: MyBuffer {
|
||||||
|
MyBuffer_T(int size): MyBuffer(size) {}
|
||||||
|
~MyBuffer_T() {}
|
||||||
|
};
|
||||||
|
|
||||||
MyBuffer_T* NewMyBuffer(int size) {
|
MyBuffer_T* NewMyBuffer(int size) {
|
||||||
auto p = new MyBuffer(size);
|
auto p = new MyBuffer_T(size);
|
||||||
return (MyBuffer_T*)(p);
|
return p;
|
||||||
}
|
}
|
||||||
void DeleteMyBuffer(MyBuffer_T* p) {
|
void DeleteMyBuffer(MyBuffer_T* p) {
|
||||||
delete p;
|
delete p;
|
||||||
@ -110,8 +116,96 @@ int MyBuffer_Size(MyBuffer_T* p) {
|
|||||||
|
|
||||||
### 将纯C接口函数转为Go函数
|
### 将纯C接口函数转为Go函数
|
||||||
|
|
||||||
|
将纯C函数包装为对应的Go函数的过程比较简单。需要注意的是,因为我们的包中包含C++11的语法,因此需要通过`#cgo CXXFLAGS: -std=c++11`打开C++11的选项。
|
||||||
|
|
||||||
|
```go
|
||||||
|
// my_buffer_capi.go
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CXXFLAGS: -std=c++11
|
||||||
|
|
||||||
|
#include "my_buffer_capi.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type cgo_MyBuffer_T C.MyBuffer_T
|
||||||
|
|
||||||
|
func cgo_NewMyBuffer(size int) *cgo_MyBuffer_T {
|
||||||
|
p := C.NewMyBuffer(C.int(size))
|
||||||
|
return (*cgo_MyBuffer_T)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgo_DeleteMyBuffer(p *cgo_MyBuffer_T) {
|
||||||
|
C.DeleteMyBuffer((*C.MyBuffer_T)(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgo_MyBuffer_Data(p *cgo_MyBuffer_T) *C.char {
|
||||||
|
return C.MyBuffer_Data((*C.MyBuffer_T)(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgo_MyBuffer_Size(p *cgo_MyBuffer_T) C.int {
|
||||||
|
return C.MyBuffer_Size((*C.MyBuffer_T)(p))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
为了区分,我们在Go中的每个类型和函数名称前面增加了`cgo_`前缀,比如cgo_MyBuffer_T是对应C中的MyBuffer_T类型。
|
||||||
|
|
||||||
|
为了处理简单,在包装纯C函数到Go函数时,除了cgo_MyBuffer_T类型本书,我们对输入参数和返回值的基础类型依然是用的C语言的类型。
|
||||||
|
|
||||||
### 包装为Go对象
|
### 包装为Go对象
|
||||||
|
|
||||||
|
在将纯C接口包装为Go函数之后,我们就可以基于包装的Go函数很容易地构造出Go对象来。因为cgo_MyBuffer_T是从C语言空间导入的类型,它无法定义自己的方法,因此我们构造了一个新的MyBuffer类型,里面的成员持有cgo_MyBuffer_T指向的C语言缓存对象。
|
||||||
|
|
||||||
|
```go
|
||||||
|
// my_buffer.go
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type MyBuffer struct {
|
||||||
|
cptr *cgo_MyBuffer_T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMyBuffer(size int) *MyBuffer {
|
||||||
|
return &MyBuffer{
|
||||||
|
cptr: cgo_NewMyBuffer(size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyBuffer) Delete() {
|
||||||
|
cgo_DeleteMyBuffer(p.cptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyBuffer) Data() []byte {
|
||||||
|
data := cgo_MyBuffer_Data(p.cptr)
|
||||||
|
size := cgo_MyBuffer_Size(p.cptr)
|
||||||
|
return ((*[1 << 31]byte)(unsafe.Pointer(data)))[0:int(size):int(size)]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
同时,因为Go语言的切片本身含有长度信息,我们将cgo_MyBuffer_Data和cgo_MyBuffer_Size两个函数合并为`MyBuffer.Data`方法,它返回一个对应底层C语言缓存空间的切片。
|
||||||
|
|
||||||
|
现在我们可以很容易在Go语言中使用包装后的缓存对象了(底层是基于C++的`std::string`实现):
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
//#include <stdio.h>
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
buf := NewMyBuffer(1024)
|
||||||
|
copy(buf.Data(), []byte("hello\x00"))
|
||||||
|
C.puts((*C.char)(unsafe.Pointer(&(buf.Data()[0]))))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
例子中,我们创建了一个1024字节大小的缓存,然后通过copy函数向缓存填充了一个字符串。为了方便C语言字符串函数处理,我们在填充字符串的默认用'\0'表示字符串结束。最后我们直接获取缓存的底层数据指针,用C语言的puts函数打印缓存的内容。
|
||||||
|
|
||||||
## Go 语言对象到 C++ 类
|
## Go 语言对象到 C++ 类
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
11
examples/ch2-05/class-cc2go/main.go
Normal file
11
examples/ch2-05/class-cc2go/main.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
//#include <stdio.h>
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
buf := NewMyBuffer(1024)
|
||||||
|
copy(buf.Data(), []byte("hello\x00"))
|
||||||
|
C.puts((*C.char)(unsafe.Pointer(&(buf.Data()[0]))))
|
||||||
|
}
|
1
examples/ch2-05/class-cc2go/my_buffer.cc
Normal file
1
examples/ch2-05/class-cc2go/my_buffer.cc
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "./my_buffer.h"
|
23
examples/ch2-05/class-cc2go/my_buffer.go
Normal file
23
examples/ch2-05/class-cc2go/my_buffer.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type MyBuffer struct {
|
||||||
|
cptr *cgo_MyBuffer_T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMyBuffer(size int) *MyBuffer {
|
||||||
|
return &MyBuffer{
|
||||||
|
cptr: cgo_NewMyBuffer(size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyBuffer) Delete() {
|
||||||
|
cgo_DeleteMyBuffer(p.cptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyBuffer) Data() []byte {
|
||||||
|
data := cgo_MyBuffer_Data(p.cptr)
|
||||||
|
size := cgo_MyBuffer_Size(p.cptr)
|
||||||
|
return ((*[1 << 31]byte)(unsafe.Pointer(data)))[0:int(size):int(size)]
|
||||||
|
}
|
19
examples/ch2-05/class-cc2go/my_buffer.h
Normal file
19
examples/ch2-05/class-cc2go/my_buffer.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct MyBuffer {
|
||||||
|
std::string* s_;
|
||||||
|
|
||||||
|
MyBuffer(int size) {
|
||||||
|
this->s_ = new std::string(size, char('\0'));
|
||||||
|
}
|
||||||
|
~MyBuffer() {
|
||||||
|
delete this->s_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Size() const {
|
||||||
|
return this->s_->size();
|
||||||
|
}
|
||||||
|
char* Data() {
|
||||||
|
return (char*)this->s_->data();
|
||||||
|
}
|
||||||
|
};
|
25
examples/ch2-05/class-cc2go/my_buffer_capi.cc
Normal file
25
examples/ch2-05/class-cc2go/my_buffer_capi.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "./my_buffer.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "./my_buffer_capi.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyBuffer_T: MyBuffer {
|
||||||
|
MyBuffer_T(int size): MyBuffer(size) {}
|
||||||
|
~MyBuffer_T() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
MyBuffer_T* NewMyBuffer(int size) {
|
||||||
|
auto p = new MyBuffer_T(size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
void DeleteMyBuffer(MyBuffer_T* p) {
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* MyBuffer_Data(MyBuffer_T* p) {
|
||||||
|
return p->Data();
|
||||||
|
}
|
||||||
|
int MyBuffer_Size(MyBuffer_T* p) {
|
||||||
|
return p->Size();
|
||||||
|
}
|
27
examples/ch2-05/class-cc2go/my_buffer_capi.go
Normal file
27
examples/ch2-05/class-cc2go/my_buffer_capi.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CXXFLAGS: -std=c++11
|
||||||
|
|
||||||
|
#include "my_buffer_capi.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type cgo_MyBuffer_T C.MyBuffer_T
|
||||||
|
|
||||||
|
func cgo_NewMyBuffer(size int) *cgo_MyBuffer_T {
|
||||||
|
p := C.NewMyBuffer(C.int(size))
|
||||||
|
return (*cgo_MyBuffer_T)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgo_DeleteMyBuffer(p *cgo_MyBuffer_T) {
|
||||||
|
C.DeleteMyBuffer((*C.MyBuffer_T)(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgo_MyBuffer_Data(p *cgo_MyBuffer_T) *C.char {
|
||||||
|
return C.MyBuffer_Data((*C.MyBuffer_T)(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cgo_MyBuffer_Size(p *cgo_MyBuffer_T) C.int {
|
||||||
|
return C.MyBuffer_Size((*C.MyBuffer_T)(p))
|
||||||
|
}
|
7
examples/ch2-05/class-cc2go/my_buffer_capi.h
Normal file
7
examples/ch2-05/class-cc2go/my_buffer_capi.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
typedef struct MyBuffer_T MyBuffer_T;
|
||||||
|
|
||||||
|
MyBuffer_T* NewMyBuffer(int size);
|
||||||
|
void DeleteMyBuffer(MyBuffer_T* p);
|
||||||
|
|
||||||
|
char* MyBuffer_Data(MyBuffer_T* p);
|
||||||
|
int MyBuffer_Size(MyBuffer_T* p);
|
Loading…
x
Reference in New Issue
Block a user