1

linux Debian Buster

go version go1.11.6 linux/amd64

gcc version 8.3.0 (Debian 8.3.0-6)

libmylib.go

package main

import "C"

import (
    "fmt"
)

func say(text string) {
    fmt.Println(text)
}

func main(){}

mylib.h

#ifndef MY_LIB_H
#define MY_LIB_H

#include <string>

void say(std::string text);

#endif

main.cpp

#include <string>
#include "mylib.h"

using namespace std;

int main() {
    string text = "Hello, world!";

    say(text);

    return 0;
}

CGO_ENABLED=1 go build -o libmylib.so -buildmode=c-shared libmylib.go

g++ -L/path/to/lib/ -lmylib main.cpp -o my-test-program

/usr/bin/ld: /tmp/ccu4fXFB.o: in function 'main': main.cpp:(.text+0x53): undefined reference to `say(std::__cxx11::basic_string<char, std::char_traits, std::allocator >)'
collect2: error: ld returned 1 exit status

with change: package main -> package mylib

CGO_ENABLED=1 go build -o libmylib.so -buildmode=c-shared libmylib.go

-buildmode=c-shared requires exactly one main package

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Егор
  • 15
  • 1
  • 6

2 Answers2

2

You have to use GoString rather than std::string in the C++ version. The error message you are getting is because of the type mismatch, manifesting as a link-time error.

See the cgo reference.

Here's a working example. There's a few differences from yours. The //export directive is needed to include the function in the generated header file, the argument is *C.char rather than string or GoString. The C++ code uses the header generated by cgo, and there has to be a const-removing cast from the static string (because go doesn't have C-like const).

libmylib.go

package main

import "C"

import (
    "fmt"
)

//export say
func say(text *C.char) {
    fmt.Println(C.GoString(text))
}

func main() {}

main.cpp

#include "libmylib.h"

int main(void) {
    say(const_cast<char*>("hello world"));

    return 0;
}

commands

This compiles to go file, generating libmylib.so and libmylib.h in the current directory.

go build -o libmylib.so -buildmode=c-shared libmylib.go

The compiles the C++ program, linking it to the shared library above:

g++ -L. main.cpp -lmylib -o hello_program

To run the program, LD_LIBRARY_PATH needs to be set to the current directory. That would be different if program was installed and the shared library put in a sensible place.

LD_LIBRARY_PATH=. ./hello_program
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
  • `go tool cgo libmylib.go` . After that i got "_obj" folder . **mylib.h** `extern void say(GoString text);` . **main.cpp** `GoString text = "Hello, World!";` . >g++ -L/path/to/lib/ -lmylib main.cpp -o my-test-program. >main.cpp:5:21: error: conversion from ‘const char [14]’ to non-scalar type ‘GoString’ {aka ‘_GoString_’} requested GoString text = "Hello, World!"; Changing to `extern void say(GoString text[]);` make no sense. – Егор Jul 19 '20 at 09:28
  • Yes, you can't create a value of type `GoString` from C++. Probably easiest is passing in a null-terminated C string (`char *` in C, `*C.char` in go), which you can convert in go to a go string using `C.GoString(s)`. – Paul Hankin Jul 19 '20 at 09:55
  • that means your go function would be: `func say(s *C.char) { fmt.Println(C.GoString(s)) }` – Paul Hankin Jul 19 '20 at 09:56
  • **libmylib.go** `func say( text *C.char ) { fmt.Println( C.GoString( text ) ) }` , **main.cpp** `char* text = "Hello, World!";` , **mylib.h** `extern void say(char* text);` . `>g++ -L/path/to/lib/ -lmylib main.cpp -o my-test-program` `>main.cpp:(.text+0x1b): undefined reference to `say(char*)'` . For creating shared objects file im using the `>CGO_ENABLED=1 go build -o libmylib.so -buildmode=c-shared libmylib.go` command. – Егор Jul 19 '20 at 10:06
  • Any reason why you're not using the generated header file, as described in the cgo reference? But I guess at this point the answer to your current problem is in Basile's answer. – Paul Hankin Jul 19 '20 at 10:27
  • @Erop There's now a complete working example in my answer. – Paul Hankin Jul 20 '20 at 09:00
0
g++ -L/path/to/lib/ -lmylib main.cpp -o test

is probably wrong. Read the invoking GCC chapter of the GCC documentation. Order of arguments to g++ matters a lot.

Also, test(1) could be some existing executable. I recommend to use some other name.

So consider compiling with

g++ -Wall -g -O main.cpp -L/path/to/lib/ -lmylib -o my-test-program

You probably want debugging information (-g), warnings (-Wall) and some optimization (-O)

You did comment

I need to use some functions from a Go file in my C ++ project.

This is curious. I assume your operating system is some Linux. Then, can't you just use inter-process communication facilities between a process running a Go program and another process running your C++ program? Consider perhaps using JSONRPC or HTTP between them. There exist several open source libraries in Go and in C++ to help you.

PS. As I commented, calling C++ code from a Go program could be much simpler. Of course you do need to read the Go documentation and the blog about cgo and probably the C++ dlopen minihowto and some C++ reference.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547