This is a creation in Article, where the information may have evolved or changed.
Working on a requirement, you need to encapsulate the functionality of a C + + dynamic library as a web interface. Having no experience with C + + development, C had a bit of experience and considered two scenarios:
- Encapsulated as PHP extension
- Using CGO in Golang
I can do both scenarios, but the final decision to adopt a 2nd scenario, the main consideration is that the Web service will eventually need to be privatized deployment to the customer, in the case of PHP, the deployment will require Nginx, FPM (of course, can also be used directly with Swoole), but the PHP code is clear, Although you can buy some commercial software for encryption (such as Swoole Compiler). If you use Golang directly, you can deploy a binary program directly to the user (need to strip off the symbolic information) on it, more convenient to deploy.
The following is a sample program that demonstrates how to call C + + through CGO in Golang.
Sample Code directory:
.├── bin│ └── cgo└── src └── cgo ├── c_src.cpp // 在Golang中调用的C函数定义 ├── c_src.h // C头文件,声明了哪些C函数会在Golang中使用,在main.go中包含 ├── main.go ├── src.cpp // C++代码 └── src.hpp // C++头文件
C_src.h Source:
#ifndef wrap_cpp_h#define wrap_cpp_h#ifdef __cplusplusextern "C" {#endif//__cplusplustypedef void* Foo;Foo foonew();void Foodestroy(Foo F);Const Char* Foogetname(Foo F, int* Retlen);void Foosetname(Foo F, Char* name);#ifdef __cplusplus}#endif//__cplusplus#endif//Wrap_cpp_h
extern "C"
function: Combining C + + and C-how does #ifdef __cplusplus work?
C_src.cpp Source:
#include "src.hpp" #include "c_src.h" #include <cstring>//Return Cxxfoo object, but convert to void*Foo foonew(){ Cxxfoo* ret = New Cxxfoo("Rokety"); return (void*)ret;}void Foodestroy(Foo F){ Cxxfoo* Foo = (Cxxfoo*)F; Delete Foo;}//Package Cxxfoo Get_name methodConst Char* Foogetname(Foo F, int* Ret_len){ Cxxfoo* Foo = (Cxxfoo*)F; STD::string name = Foo -get_name(); *Ret_len = name.length(); Const Char* Ret_str = (Const Char*)malloc(*Ret_len); memcpy((void*)Ret_str, name.C_str(), *Ret_len); return Ret_str;}//Package Cxxfoo Set_name methodvoid Foosetname(Foo F, Char* name){ Cxxfoo* Foo = (Cxxfoo*)F; STD::string _name(name, strlen(name)); Foo -Set_name(_name);}
C_src.cpp Possible questions:
- Why do I need to define Foo? Because there is no concept of class in C, it is necessary to convert the C + + class to the data type in C
- Why are malloc and memcpy required in Foogetname? Because name is a local variable and memory is allocated on the stack, the memory occupied by name is freed when the CGO call returns.
Main.go Source:
Package Main//#include "c_src.h"//#include <stdlib.h>Import "C"Import ("FMT""unsafe")type Gofoo struct {Foo C.Foo}func Newgofoo() Gofoo {var ret Gofooret.Foo = C.foonew()return ret}func (F Gofoo) Destroy() {C.Foodestroy(F.Foo)}func (F Gofoo) GetName() string {Rlen := C.int(0)name := C.Foogetname(F.Foo, &Rlen)defer C. Free(unsafe.Pointer(name)) //must use the free function of C to release the memory of malloc in Foogetnamereturn C.GOSTRINGN(name, Rlen) //Constructs a Golang string value from name}func (F Gofoo) SetName(name string) {CNAME := C.CString(name) //Convert the Golang string value to the char* type value in C, where the malloc to C is calledC.Foosetname(F.Foo, CNAME)C. Free(unsafe.Pointer(CNAME)) //Release the memory above malloc}func Main() {Foo := Newgofoo()FMT.Println(Foo.GetName())Foo.GetName()Foo.SetName("New Rokety")FMT.Println(Foo.GetName())Foo.Destroy()}
Main.go Possible questions:
- Unsafe. Pointer (...) Equivalent to converting a variable to a void* type in C
- Why conversion is required in SetName because the memory of the name variable is allocated in Golang, and the string type is non-modifiable, so the memory required for name is allocated in C for use in Foosetname
- One thing to note is that the
import "C"
above must be followed by // #include ...
comments
SRC.HPP Source:
#ifndef cxx_h#define cxx_h#include <string>class Cxxfoo{ Public: Cxxfoo(STD::string name); ~Cxxfoo(); STD::string get_name(); void Set_name(STD::string name);Private: STD::string name;};#endif//Cxx_h
Src.cpp Source
#include "src.hpp" #include <iostream>Cxxfoo::Cxxfoo(STD::string name){ This -name = name;}Cxxfoo::~Cxxfoo(){}STD::string Cxxfoo::get_name(){ return This -name;}void Cxxfoo::Set_name(STD::string name){ This -name = name;}
Summary:
- The data type in C corresponds to the C.XXX data type of Golang: CGO type (CGO Types)
- The memory that is requested in C + + has to be released
- You can
import "C"
add a corresponding comment to a dynamic library that needs to be linked or added with a compile parameter// #cgo CFLAGS: -DPNG_DEBUG=1
Resources:
- How do I use C + + in Go?
- Command CGO
- C? Go? Cgo!
- Golang CGO Programming Call returns the C function library for char* pointers and lengths
- Cgo:go and C Interoperability Technology (i): Go tune C Fundamentals