I'm writing a native library which provides a C api using cgo. I have a C header file which includes a constant definition, but when I include this in a package using cgo, and then import this package into main in go, I get a multiple definition...
linker error on build:
C:\Users\danie\AppData\Local\Temp\go-link-1146178960\000011.o:C:\Temp\cgo-error\mypackage/errors.h:29: multiple definition of `MYMODULE_ERROR_STRS'; C:\Users\danie\AppData\Local\Temp\go-link-1146178960\000010.o:C:/Temp/cgo-error/mypackage/errors.h:29: first defined here
collect2.exe: error: ld returned 1 exit status
The example code below uses the error handling sample given in this answer: https://stackoverflow.com/a/59221452/1324919
main.go:
package main
import "C"
import "example.com/cgo-error/mypackage"
func main() {}
//export exported_function
func exported_function() {
mypackage.CallCFunction()
}
mypackage.go (includes the C header file):
package mypackage
// #include "errors.h"
import "C"
func CallCFunction() {
_ = C.mymodule_func1()
}
errors.h:
#ifndef ERRORS_H_
#define ERRORS_H_
/// @brief Error codes for library "mymodule"
typedef enum mymodule_error_e
{
/// No error
MYMODULE_ERROR_OK = 0,
MYMODULE_ERROR_INVARG,
MYMODULE_ERROR_NOMEM,
MYMODULE_ERROR_MYERROR,
MYMODULE_ERROR_COUNT,
} mymodule_error_t;
// Array of strings to map enum error types to printable strings
const char *const MYMODULE_ERROR_STRS[] =
{
"MYMODULE_ERROR_OK",
"MYMODULE_ERROR_INVARG",
"MYMODULE_ERROR_NOMEM",
"MYMODULE_ERROR_MYERROR",
};
// To get a printable error string
const char *mymodule_error_str(mymodule_error_t err);
// Other functions in mymodule
mymodule_error_t mymodule_func1(void);
mymodule_error_t mymodule_func2(void);
mymodule_error_t mymodule_func3(void);
#endif
errors.c:
#include "errors.h"
#include <stdio.h>
/// @brief Function to get a printable string from an enum error type
/// @param[in] err a valid error code for this module
/// @return A printable C string corresponding to the error code input above, or NULL if an invalid error code
/// was passed in
const char *mymodule_error_str(mymodule_error_t err)
{
const char *err_str = NULL;
// Ensure error codes are within the valid array index range
if (err >= MYMODULE_ERROR_COUNT)
{
goto done;
}
err_str = MYMODULE_ERROR_STRS[err];
done:
return err_str;
}
// Let's just make some empty dummy functions to return some errors; fill these in as appropriate for your
// library module
mymodule_error_t mymodule_func1(void)
{
return MYMODULE_ERROR_OK;
}
mymodule_error_t mymodule_func2(void)
{
return MYMODULE_ERROR_INVARG;
}
mymodule_error_t mymodule_func3(void)
{
return MYMODULE_ERROR_MYERROR;
}
It looks to me that having the header file included in mypackage.go
and also including it in errors.c
causes it to be imported twice by the go linker when mypackage
is imported in main
.
Is there anyway to get around this?
Do not define variables in your .h files as you will a new instance of those variables in every compilation unit which #include
this .h file It is the source of your problem. Place them in the C file.
In the errors.h file:
extern const char *const MYMODULE_ERROR_STRS[];
and the actual definition in the errors.c: file:
const char *const MYMODULE_ERROR_STRS[] =
{
"MYMODULE_ERROR_OK",
"MYMODULE_ERROR_INVARG",
"MYMODULE_ERROR_NOMEM",
"MYMODULE_ERROR_MYERROR",
};