Search code examples
c++cheader-files

extern "C": What does and what doesn't need it?


There are already questions about extern "C" on functions, but this question tries to extend it to other things such as variables and more.

If I have a foo.hpp header to use a foo.c file from C++, I made this template which I filled with simple examples for this question:

/******************************************************************************
 ******* include guard { ******************************************************
 ******************************************************************************/
#ifndef FOO_HPP
#define FOO_HPP


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <cstdbool>
#include <cstddef>
#include <cstdint>


/******************************************************************************
 ******* typedefs *************************************************************
 ******************************************************************************/
/* `#if !defined(UINT128_MAX)` is to test if uint128_t already exists */
#if !defined(UINT128_MAX)
    typedef __uint128_t uint128_t;
    typedef __int128_t  int128_t;
#endif


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
#if !defined(UINT128_MAX)
    #define UINT128_MAX (~((uint128_t)0))
#endif
#if !defined(INT128_MAX)
    #define INT128_MAX  ((int128_t)(UINT128_MAX >> 1))
#endif
#if !defined(INT128_MIN)
    #define INT128_MIN  ((int128_t)(-INT128_MAX - 1))
#endif


/******************************************************************************
 ******* enums ****************************************************************
 ******************************************************************************/
enum    Some_Enum {
    SOME_CONSTANT_A,
    SOME_CONSTANT_B,

    SOME_CONSTANT_C
};


/******************************************************************************
 ******* structs / unions *****************************************************
 ******************************************************************************/
union   Some_Union {
    int128_t    a;
    int64_t     b[SOME_CONSTANT_C];
};

struct  Some_Struct {
    union Some_Union    a;
    bool                b;
};


/******************************************************************************
 ******* static const variables ***********************************************
 ******************************************************************************/
static const    struct Some_Struct  x = {
    .a  = {
        .b  = {
            [SOME_CONSTANT_A]   = 0,
            [SOME_CONSTANT_B]   = 1
        }
    },
    .b  = true
};


/******************************************************************************
 ******* C wrapper { **********************************************************
 ******************************************************************************/
extern  "C" {


/******************************************************************************
 ******* extern variables *****************************************************
 ******************************************************************************/
extern  union Some_Union    y;


/******************************************************************************
 ******* extern functions *****************************************************
 ******************************************************************************/
int foo(size_t n, int128_t arr[restrict]);


/******************************************************************************
 ******* } C wrapper **********************************************************
 ******************************************************************************/
}   /* extern "C" */


/******************************************************************************
 ******* static inline functions (prototypes) *********************************
 ******************************************************************************/
static inline   int compare_ldbl(const void *a_p, const void *b_p);


/******************************************************************************
 ******* static inline functions (definitions) ********************************
 ******************************************************************************/
static inline   int compare_ldbl(const void *a_p, const void *b_p)
{
    long double a = *(long double *)a_p;
    long double b = *(long double *)b_p;

    if (a < b)
        return  -1;
    else if (a > b)
        return  1;
    else
        return  0;
}


/******************************************************************************
 ******* } include guard ******************************************************
 ******************************************************************************/
#endif      /* foo.hpp */


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

All the types, macros, enums, structs, unions, and extern variables and functions should be compatible with C (as much as possible). Only static things can behave different, because I can tune them for C++ just in the header.

Is that the correct place to put the C wrapper?


Solution

  • extern "C" declares a function type, function, or variable to have C language linkage [dcl.link]/1. You need extern "C" if you want to make a function or variable defined in your C++ code usable for C code, or if you want to make your C++ code use a function or variable that is defined in C code. That's it. This is basically all the C++ language has to say about the matter.

    Contrary to popular belief, the purpose of extern "C" is not to inhibit name mangling. Name mangling is not a concept that exists in the C++ language and, therefore, there cannot possibly be a way to explicitly control it from within the C++ language. Name mangling is a mechanism employed by many (basically all) C++ implementations when they map your C++ code to a given target platform. It's an implementation detail. The purpose of extern "C" is to instruct the compiler to do whatever it is that needs to be done to make the given entities link with C code on the target platform. With many ABIs, that implies no name mangling. But with some ABIs, e.g., the Windows ABI, even C functions are subject to name mangling (in this particular case due to the presence of different calling conventions). Use extern "C" when you want to link C++ code with C code. That's its purpose and that's all it's good for. If you want to control the names of exported or imported symbols, use whatever is the way to do so according to the documentation of your toolchain for the given target platform, which will typically be stuff like Module Definition Files, linker flags, …