This is an example of Go code calling to a C++ library with a C wrapper.
go build # this only ensures it compilesgo test # actually runs tests| // num.cpp | |
| #include "nummer.hpp" | |
| #include "num.h" | |
| Num NumInit() { | |
| cxxNum * ret = new cxxNum(1); | |
| return (void*)ret; | |
| } | |
| void NumFree(Num n) { | |
| cxxNum * num = (cxxNum*)n; | |
| delete num; | |
| } | |
| void NumIncrement(Num n) { | |
| cxxNum * num = (cxxNum*)n; | |
| num->Increment(); | |
| } | |
| int NumGetValue(Num n) { | |
| cxxNum * num = (cxxNum*)n; | |
| return num->GetValue(); | |
| } |
| package num | |
| // #cgo LDFLAGS: -L. -lstdc++ | |
| // #cgo CXXFLAGS: -std=c++14 -I. | |
| // #include "num.h" | |
| import "C" | |
| import "unsafe" | |
| type GoNum struct { | |
| num C.Num | |
| } | |
| func New() GoNum { | |
| var ret GoNum | |
| ret.num = C.NumInit() | |
| return ret | |
| } | |
| func (n GoNum) Free() { | |
| C.NumFree((C.Num)(unsafe.Pointer(n.num))) | |
| } | |
| func (n GoNum) Inc() { | |
| C.NumIncrement((C.Num)(unsafe.Pointer(n.num))) | |
| } | |
| func (n GoNum) GetValue() int { | |
| return int(C.NumGetValue((C.Num)(unsafe.Pointer(n.num)))) | |
| } |
| // num.h | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| typedef void* Num; | |
| Num NumInit(void); | |
| void NumFree(Num); | |
| void NumIncrement(Num); | |
| int NumGetValue(Num); | |
| #ifdef __cplusplus | |
| } | |
| #endif |
| package num | |
| import ( | |
| "reflect" | |
| "testing" | |
| ) | |
| func TestNum(t *testing.T) { | |
| num := New() | |
| num.Inc() | |
| if num.GetValue() != 2 { | |
| t.Error("unexpected value received") | |
| } | |
| num.Inc() | |
| num.Inc() | |
| num.Inc() | |
| if num.GetValue() != 5 { | |
| t.Error("unexpected value received") | |
| } | |
| value := num.GetValue() | |
| num.Free() | |
| typ := reflect.TypeOf(value) | |
| if typ.Name() != "int" { | |
| t.Error("got unexpected type") | |
| } | |
| } |
| #include <iostream> | |
| #include "nummer.hpp" | |
| void cxxNum::Increment(void) { | |
| this->value++; | |
| } | |
| int cxxNum::GetValue(void) { | |
| return this->value; | |
| } |
| class cxxNum { | |
| private: | |
| int value; | |
| public: | |
| cxxNum(int _num):value(_num){}; | |
| ~cxxNum(){}; | |
| void Increment(void); | |
| int GetValue(void); | |
| }; |
I feel that there is no difference between this example and using C. Is there any code using std::string and std::vector?
I feel that there is no difference between this example and using C. Is there any code using
std::stringandstd::vector?
obvious - in this example c++ code wrapped in c functions, therefore use conteiners in c++ part and wrap it in c code.
How can I make it work if I put the nummer.hpp and nummer.cpp file to a sub folder?
├── go.mod
└── num
├── num.cpp
├── num.go
├── num.h
├── nummer
│ ├── nummer.cpp
│ └── nummer.hpp
└── num_test.go
The num.cpp is like:
#include "nummer/nummer.hpp"
#include "num.h"
// ......I get some error:
/usr/lib/go-1.20/pkg/tool/linux_amd64/link: running g++ failed: exit status 1
/usr/bin/ld: /tmp/go-link-1235907040/000002.o: in function `NumIncrement':
/Go/cpp/num/num.cpp:16: undefined reference to `cxxNum::Increment()'
/usr/bin/ld: /tmp/go-link-1235907040/000002.o: in function `NumGetValue':
/Go/cpp/num/num.cpp:21: undefined reference to `cxxNum::GetValue()'
collect2: error: ld returned 1 exit status
Thank you so much. It is great example for me