How to use C++ in Go
Asked Answered
D

12

213

In the new Go language, how do I call C++ code? In other words, how can I wrap my C++ classes and use them in Go?

Daladier answered 11/11, 2009 at 5:25 Comment(9)
In the tech talk SWIG was very briefly mentioned, something like "..until we get the swig done.."Surah
@Matt: Likely he wants to use an existing C++ library without having to port it to C or Go. I wanted the same thing.Loehr
I can't think of a single decent library available for C++ and not for C. I'd love to know what you have in mind.Trimer
@Matt: One example is the Boost library, and there are thousands of other useful C++ libraries. But maybe I'm just feeding a troll here ...Daladier
@Matt: in my case, I wanted to make a Go interface to our existing client library but the library is mainly C++. Porting it to C or Go is simply not an option.Loehr
@Graeme Perrow: Try giving it a C interface.Trimer
@Matt: Our library has a C interface but loading the DLL, implemented in C++, would not work because the C++ runtime stuff was missing.Loehr
Can I use SWIG with windows C++ code?Greatgrandaunt
github.com/charleslaw/static-cgo is a working exampleLogos
K
189

Update: I've succeeded in linking a small test C++ class with Go

If you wrap you C++ code with a C interface you should be able to call your library with cgo (see the example of gmp in $GOROOT/misc/cgo/gmp).

I'm not sure if the idea of a class in C++ is really expressible in Go, as it doesn't have inheritance.

Here's an example:

I have a C++ class defined as:

// foo.hpp
class cxxFoo {
public:
  int a;
  cxxFoo(int _a):a(_a){};
  ~cxxFoo(){};
  void Bar();
};

// foo.cpp
#include <iostream>
#include "foo.hpp"
void
cxxFoo::Bar(void){
  std::cout<<this->a<<std::endl;
}

which I want to use in Go. I'll use the C interface

// foo.h
#ifdef __cplusplus
extern "C" {
#endif
  typedef void* Foo;
  Foo FooInit(void);
  void FooFree(Foo);
  void FooBar(Foo);
#ifdef __cplusplus
}
#endif

(I use a void* instead of a C struct so the compiler knows the size of Foo)

The implementation is:

//cfoo.cpp
#include "foo.hpp"
#include "foo.h"
Foo FooInit()
{
  cxxFoo * ret = new cxxFoo(1);
  return (void*)ret;
}
void FooFree(Foo f)
{
  cxxFoo * foo = (cxxFoo*)f;
  delete foo;
}
void FooBar(Foo f)
{
  cxxFoo * foo = (cxxFoo*)f;
  foo->Bar();
}

with all that done, the Go file is:

// foo.go
package foo
// #include "foo.h"
import "C"
import "unsafe"
type GoFoo struct {
     foo C.Foo;
}
func New()(GoFoo){
     var ret GoFoo;
     ret.foo = C.FooInit();
     return ret;
}
func (f GoFoo)Free(){
     C.FooFree(unsafe.Pointer(f.foo));
}
func (f GoFoo)Bar(){
     C.FooBar(unsafe.Pointer(f.foo));
}

The makefile I used to compile this was:

// makefile
TARG=foo
CGOFILES=foo.go
include $(GOROOT)/src/Make.$(GOARCH)
include $(GOROOT)/src/Make.pkg
foo.o:foo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $<
cfoo.o:cfoo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $<
CGO_LDFLAGS+=-lstdc++
$(elem)_foo.so: foo.cgo4.o foo.o cfoo.o
    gcc $(_CGO_CFLAGS_$(GOARCH)) $(_CGO_LDFLAGS_$(GOOS)) -o $@ $^ $(CGO_LDFLAGS)

Try testing it with:

// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
    foo := New();
    foo.Bar();
    foo.Free();
}

You'll need to install the shared library with make install, then run make test. Expected output is:

gotest
rm -f _test/foo.a _gotest_.6
6g -o _gotest_.6 foo.cgo1.go foo.cgo2.go foo_test.go
rm -f _test/foo.a
gopack grc _test/foo.a _gotest_.6  foo.cgo3.6
1
PASS
Kinnon answered 12/11, 2009 at 10:12 Comment(7)
Be careful with this, I've got no idea what might happen to memory if you send it between the two languages.Kinnon
I gotta say, this example reminds me why I want to write pure Go. Look how much bigger and uglier the C++ side is. Ick.Balthazar
@ScottWales any chance you might have put this in a repo on Github or anything? I would love to see a working exampleJugoslavia
@Arne: You don't downvote an answer because it's not the best. You downvote an answer because it's not helpful. As long as it works, this answer is still helpful even if there are better solutions.Loehr
Good news, Go will compile cpp now so the makefile is no longer required. The unsafe.Pointer wrappers did not work for me. A slight modification compiled for me: play.golang.org/p/hKuKV51cRp go test should work without the makefileLacrimatory
I'd avoid the typedef void* Foo and use an opaque handle type, and keep it as a pointer type to be clear what you're doing. I wrote a reasonably complete version of this in https://mcmap.net/q/128632/-developing-c-wrapper-api-for-object-oriented-c-codeBidwell
FYI In go1.7 I can use this code without any Makefile and "go test" just works. No shared library necessary.Intramundane
F
66

Seems that currently SWIG is best solution for this:

https://www.swig.org/Doc4.0/Go.html

It supports inheritance and even allows to subclass C++ class with Go struct so when overridden methods are called in C++ code, Go code is fired.

Section about C++ in Go FAQ is updated and now mentions SWIG and no longer says "because Go is garbage-collected it will be unwise to do so, at least naively".

Forayer answered 29/3, 2012 at 19:0 Comment(2)
I wish there was a way to bump this up. The other answers are outdated. Plus SWIG has versioned up swig.org/Doc3.0/Go.htmlTanker
@Tanker ... and a few years later, now there's version 4. :-)Passerine
S
38

As of go1.2+, cgo automatically incorporates and compiles C++ code:

https://go.dev/doc/go1.2#cgo_and_cpp

Subdual answered 23/4, 2014 at 20:17 Comment(0)
C
35

You can't quite yet from what I read in the FAQ:

Do Go programs link with C/C++ programs?

There are two Go compiler implementations, gc (the 6g program and friends) and gccgo. Gc uses a different calling convention and linker and can therefore only be linked with C programs using the same convention. There is such a C compiler but no C++ compiler. Gccgo is a GCC front-end that can, with care, be linked with GCC-compiled C or C++ programs.

The cgo program provides the mechanism for a “foreign function interface” to allow safe calling of C libraries from Go code. SWIG extends this capability to C++ libraries.

Cease answered 11/11, 2009 at 5:30 Comment(0)
P
22

I've created the following example based on Scott Wales' answer. I've tested it in macOS High Sierra 10.13.3 running go version go1.10 darwin/amd64.

(1) Code for library.hpp, the C++ API we aim to call.

#pragma once
class Foo {
 public:
  Foo(int value);
  ~Foo();
  int value() const;    
 private:
  int m_value;
};

(2) Code for library.cpp, the C++ implementation.

#include "library.hpp"
#include <iostream>

Foo::Foo(int value) : m_value(value) {
  std::cout << "[c++] Foo::Foo(" << m_value << ")" << std::endl;
}

Foo::~Foo() { std::cout << "[c++] Foo::~Foo(" << m_value << ")" << std::endl; }

int Foo::value() const {
  std::cout << "[c++] Foo::value() is " << m_value << std::endl;
  return m_value;
}

(3) Code for library-bridge.h the bridge needed to expose a C API implemented in C++ so that go can use it.

#pragma once
#ifdef __cplusplus
extern "C" {
#endif

void* LIB_NewFoo(int value);
void LIB_DestroyFoo(void* foo);
int LIB_FooValue(void* foo);

#ifdef __cplusplus
}  // extern "C"
#endif

(4) Code for library-bridge.cpp, the implementation of the bridge.

#include <iostream>

#include "library-bridge.h"
#include "library.hpp"

void* LIB_NewFoo(int value) {
  std::cout << "[c++ bridge] LIB_NewFoo(" << value << ")" << std::endl;
  auto foo = new Foo(value);
  std::cout << "[c++ bridge] LIB_NewFoo(" << value << ") will return pointer "
            << foo << std::endl;
  return foo;
}

// Utility function local to the bridge's implementation
Foo* AsFoo(void* foo) { return reinterpret_cast<Foo*>(foo); }

void LIB_DestroyFoo(void* foo) {
  std::cout << "[c++ bridge] LIB_DestroyFoo(" << foo << ")" << std::endl;
  AsFoo(foo)->~Foo();
}

int LIB_FooValue(void* foo) {
  std::cout << "[c++ bridge] LIB_FooValue(" << foo << ")" << std::endl;
  return AsFoo(foo)->value();
}

(5) Finally, library.go, the go program calling the C++ API.

package main

// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
import "unsafe"
import "fmt"

type Foo struct {
    ptr unsafe.Pointer
}

func NewFoo(value int) Foo {
    var foo Foo
    foo.ptr = C.LIB_NewFoo(C.int(value))
    return foo
}

func (foo Foo) Free() {
    C.LIB_DestroyFoo(foo.ptr)
}

func (foo Foo) value() int {
    return int(C.LIB_FooValue(foo.ptr))
}

func main() {
    foo := NewFoo(42)
    defer foo.Free() // The Go analog to C++'s RAII
    fmt.Println("[go]", foo.value())
}

Using the following Makefile

liblibrary.so: library.cpp library-bridge.cpp
    clang++ -o liblibrary.so library.cpp library-bridge.cpp \
    -std=c++17 -O3 -Wall -Wextra -fPIC -shared

I can run the example program as follows:

$ make
clang++ -o liblibrary.so library.cpp library-bridge.cpp \
    -std=c++17 -O3 -Wall -Wextra -fPIC -shared
$ go run library.go
[c++ bridge] LIB_NewFoo(42)
[c++] Foo::Foo(42)
[c++ bridge] LIB_NewFoo(42) will return pointer 0x42002e0
[c++ bridge] LIB_FooValue(0x42002e0)
[c++] Foo::value() is 42
[go] 42
[c++ bridge] LIB_DestroyFoo(0x42002e0)
[c++] Foo::~Foo(42)

Important

The comments above import "C" in the go program are NOT OPTIONAL. You must put them exactly as shown so that cgo knows which header and library to load, in this case:

// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"

Link to GitHub repo with the full example.

Pincince answered 27/2, 2018 at 20:8 Comment(2)
Thank you - this was very helpful!Overglaze
That works only for shared library not with static libraries.Punitive
M
12

Looks it's one of the early asked question about Golang . And same time answers to never update . During these three to four years , too many new libraries and blog post has been out . Below are the few links what I felt useful .

SWIG and Go

Calling C++ Code From Go With SWIG

On comparing languages, C++ and Go

GoForCPPProgrammers

Maltreat answered 10/6, 2014 at 19:14 Comment(0)
C
3

There's talk about interoperability between C and Go when using the gcc Go compiler, gccgo. There are limitations both to the interoperability and the implemented feature set of Go when using gccgo, however (e.g., limited goroutines, no garbage collection).

Chelsiechelsy answered 11/11, 2009 at 17:47 Comment(1)
1. Make a language with no facilities for manual memory management, 2. Remove garbage collection? Am I the only one scratching my head at this?Parrett
Z
2

You're walking on uncharted territory here. Here is the Go example for calling C code, perhaps you can do something like that after reading up on C++ name mangling and calling conventions, and lots of trial and error.

If you still feel like trying it, good luck.

Zoolatry answered 11/11, 2009 at 5:59 Comment(0)
J
2

You might need to add -lc++ to the LDFlags for Golang/CGo to recognize the need for the standard library.

Jeggar answered 12/1, 2017 at 14:10 Comment(0)
R
1

The problem here is that a compliant implementation does not need to put your classes in a compile .cpp file. If the compiler can optimize out the existence of a class, so long as the program behaves the same way without it, then it can be omitted from the output executable.

C has a standardized binary interface. Therefore you'll be able to know that your functions are exported. But C++ has no such standard behind it.

Rolon answered 11/11, 2009 at 5:35 Comment(0)
P
0

Funny how many broader issues this announcement has dredged up. Dan Lyke had a very entertaining and thoughtful discussion on his website, Flutterby, about developing Interprocess Standards as a way of bootstrapping new languages (and other ramifications, but that's the one that is germane here).

Protist answered 13/11, 2009 at 0:14 Comment(0)
M
-2

This can be achieved using command cgo.

In essence 'If the import of "C" is immediately preceded by a comment, that comment, called the preamble, is used as a header when compiling the C parts of the package. For example:'
source:https://golang.org/cmd/cgo/

// #include <stdio.h>
// #include <errno.h>
import "C"
Massicot answered 21/9, 2019 at 6:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.