Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

External linkage and »extern "C"« block

Tags:

c++

c

I have an int ID, which I want to define in C++ and make available for C linkage (contrived case for the sake of simplicity):

/* i.h */
#ifdef __cplusplus
extern "C" {
#endif
        extern int ID;
#ifdef __cplusplus
}
#endif

Here's a C and a C++ program using the int:

/* m.cpp */
#include "i.h"
#include <iostream>
int main() { std::cout << ID << std::endl; }

/* m.c */
#include "i.h"
#include <stdio.h>
int main() { printf("%d\n", ID); }

Now what I'm wondering is the syntax of extern "C" and/or extern. Here's how int ID can and cannot be defined:

/* i.cpp */
//                     const int ID = 88;   // no C linkage, obviously, LNK2019/1120
// extern "C"          const int ID = 88;   // provides C linkage
// extern "C" {        const int ID = 88; } // no C linkage, surprisingly, LNK2019/1120
// extern "C" { extern const int ID = 88; } // C linkage restored

Compiling:

cl /nologo /W4 m.cpp i.cpp /MD /EHsc
cl /nologo /W4 m.c   i.cpp /MD

What I don't understand is the syntax when extern "C" is used with a {block}. I have to repeat the extern, whereas with the blockless form of extern "C" I do not. Is this just a syntax quirk or is there more to it?

I stumbled upon this issue on page 98 of Inside COM by Dale Rogerson. There is a code listing with the nested extern and a comment intended to clarify (but which I don't understand):

#include <objbase.h>
extern "C"{
  extern const IID IID_IX = {0x32bb8320, 0x41b, 0x11cf, ...};
  // The extern is required to allocate memory for C++ constants.
}

Can anyone explain the internal extern?

like image 676
Lumi Avatar asked May 27 '14 13:05

Lumi


3 Answers

The inner extern means what expected: "the symbol is defined somewhere else, it has external linkage, don't allocate space for it in this unit (unless defined here as well)".

The outer extern "C" { ... } has maybe a bit misleading syntax, it only tells the compiler "if you are creating names of any symbols with external linkage inside this block, use traditional C naming (C language linkage) instead of C++ name mangling (C++ language linkage) for them". But it does not specify that all things inside the block are "defined somewhere else" (have external linkage) -- they are still declared with the default internal linkage. That's why you need the inner extern as well.

The one-line variant extern "C" <variable declaration> is just a shorthand, as you probably want to define an external variable if you care about its cross-unit symbol name.

(As a side-note, I would include i.h also in i.cpp, that way I wouldn't have to remember to mess with extern "C" any more in the implementation.)

like image 53
Yirkha Avatar answered Oct 22 '22 16:10

Yirkha


The problem is that const and extern are warring with each other. const means (among other things), "Make this definition internal, unless there's an explicit extern on it.". When the definition is in an extern "C" block, there's no extern directly on the definition, so the "implicit internal" from the const takes precedence. The other definitions all have an extern directly on the definition, so it becomes external.

like image 37
Chris Dodd Avatar answered Oct 22 '22 17:10

Chris Dodd


Here's how int ID can and cannot be defined:

Careful! extern "C" has different effects in different contexts.

N3797 §7.5/7:

A declaration directly contained in a linkage-specification is treated as if it contains the extern specifier (7.1.1) for the purpose of determining the linkage of the declared name and whether it is a definition. Such a declaration shall not specify a storage class.

extern "C" int i; // declaration
extern "C" {
    int i;           // definition
}

So the second line in your snippet is just a declaration whilst your third line is also a definition. This changes what effect the linkage-specification has on the declarations - you can't give a name defined in a C++ translation unit C linkage. You have to make it a pure declaration.

like image 2
Columbo Avatar answered Oct 22 '22 16:10

Columbo