3

I'm trying to include C++ code in my Go code, but isn't recognized.

I first thought that it considers it as C code and try (and fail) to compile as such, but removing the include line actually gives me c++ error troubleshooting like this error: ‘cout’ is not a member of ‘std’ The code compiles correctly with g++.

I have tried to add the -lstdc++ LDLFLAG, and add the path to the lib in CXXFLAG but it doesn't change a thing.

I have made some other tests (and all fail) but this is the smallest one.

This is the c++ files

test.cpp

#include "test.hpp"
    int test() 
    {
        std::cout << "Hello, World! ";
        return 0;
    }

test.hpp 
#include <iostream>
int test() ;

And this is my go file

//#cgo CXXFLAGS: -I/usr/lib/
//#cgo LDFLAGS: -L/usr/lib/ -lstdc++
//#include "test.hpp"
import "C"

func main() {
    C.test()
}

I compile using go build but I have also tried to use env CGO_ENABLED CGO_CXXFLAGS="-std=c++11" go build (the env part is fish specific) and it returns the same error.

It's supposed to compile correctly, but instead I have iostream: No such file or directory.

EDIT : I tried to add CFLAGS: -x c++ as suggested in the comments, the compiler searches at the right place, but I get another error invalid conversion from ‘void*’ to ‘_cgo_96e70225d9dd_Cfunc_test(void*)::<unnamed struct>*’ [-fpermissive] and I don't know if it's related to this new flafg

kaios
  • 395
  • 1
  • 4
  • 13
  • 2
    I don't see evidence that it's "trying to compile it as C code", only that it cannot find one of the standard library headers. – Lightness Races in Orbit Jul 12 '19 at 10:00
  • This isn't the only code I've tried to compile and it doesn't understand c++ syntax, I'll edit my question – kaios Jul 12 '19 at 10:05
  • You should also show how you trigger this process, i.e. what commands you use. – Lightness Races in Orbit Jul 12 '19 at 10:17
  • Actually you were right, it doesn't have anything to do with C code, I corrected the question, thank you very much – kaios Jul 12 '19 at 10:26
  • 1
    Use `#cgo CFLAGS: -x c++` and `#cgo LDFLAGS: -lstdc++` – georgeok Jul 12 '19 at 10:28
  • Try adding `--verbose` to `CXXFLAGS` (or whatever fits your compiler) and then pass the `-x` command-line option to `go build` (or `go install`—whatever you're using). Then see what the C++ compiler writes out about how it processed its input files. (Well, and a silly but obligatory question: is that C++ code compilable "as is"? I mean directly, not via the `go` toolchain.) – kostix Jul 12 '19 at 10:32
  • On a side note: if you intend to call functions of your C++ code from Go, you have to put their declarations in the `extern "C" { ... }` blocks to inhibit [name mangling](https://en.wikipedia.org/wiki/Name_mangling) which will make the exported functions unavailable for linking with the Go code. (More info [here](https://en.cppreference.com/w/cpp/language/language_linkage)). – kostix Jul 12 '19 at 10:34
  • @georgeok thanks, I tried that, and it's correctly calling cc1plus and not cc1 but I have another error now, it's in my edited answer – kaios Jul 12 '19 at 12:00
  • @kostix thank you, it's a bit clearer now with the -v option And yes, it is compilable "as is", I said that it compiles correctly with g++. – kaios Jul 12 '19 at 12:09
  • So, what's the exact error now? That one about invalid cast or about missing ``? If it's the former, have you applied proper external linkage rule to your function as suggested in my first comment? – kostix Jul 12 '19 at 12:46
  • Right now, the issue is resolved, but I'm not sure if the invalid cast is related to the solution given or not. And yes I have applied the extern "C" around the declaration, but it doesn't change a thing to the invalid cast error – kaios Jul 12 '19 at 12:55

1 Answers1

5

cgo makes it very easy to wrap C with Go, but C++ is a bit different. You have to extern "C" the functions that you want to make a function-name in C++ have 'C' linkage, otherwise the linker won't see the function. So, the actual problem is in the C++ header file. If you can't change the C++ code because it's a library, you may have to write wrappers (example).

This will compile:

.
├── test.cpp
├── test.go
└── test.hpp

test.hpp

#ifdef __cplusplus
extern "C" {
#endif

    int test();
#ifdef __cplusplus
}
#endif

test.cpp

#include <iostream>
#include "test.hpp"
int test() {
    std::cout << "Hello, World! ";
    return 0;
}

test.go

package main

// #cgo CXXFLAGS: -I/usr/lib/
// #cgo LDFLAGS: -L/usr/lib/ -lstdc++
// #include "test.hpp"
import "C"

func main() {
    C.test()
}

Put the files in the same folder, run go build

Hello, World!

georgeok
  • 5,321
  • 2
  • 39
  • 61
  • Thank you for your answer, it finally works. However, why would you put `#include ` in the .cpp and not the .hpp? – kaios Jul 12 '19 at 14:36
  • 1
    I believe in C++ most people put the includes that are required in the headers and whatever is only required in the cpp files there – georgeok Jul 12 '19 at 14:42
  • I see now, but why does it give me the same error as before (`iostream: No such file or directory`) when I put it back in the hpp? (even if I put it inside the extern "C") – kaios Jul 12 '19 at 14:57
  • tbh I am not sure about that. Probably it's because we import it in the test.go file and that only expect C-like code – georgeok Jul 12 '19 at 15:01
  • So say I have c++ specific code in the hpp, I won't be able to make it work, right? Because that was my issue from the get-go – kaios Jul 12 '19 at 15:07
  • I think so. That's probably why people end up writing wrappers. – georgeok Jul 12 '19 at 15:10