cgolinkercgo

cgo: How to prevent "multiple definition..." error when using cgo to include a C header file in a package?


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?


Solution

  • 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",
    };