tags:

views:

120

answers:

3

I am trying to interface to Ada in C++ using externs. What is the difference between these two implementations?

Implementation A

namespace Ada
{
    extern "C"
    {
        int getNumber();
        int index;
        int value;
    }
}

Implementation B

namespace Ada
{
    extern "C"
    {
        int getNumber();
    }
    extern "C" int index;
    extern "C" int value;
}

Both implementations compile just fine. But Impl-A fails to link, I get a multiple definition error for index and value. I'm just trying to understand the differences.

+2  A: 

I'm not sure why the second works, but you want

namespace Ada
{
    extern "C"
    {
        int getNumber();
        extern int index;
        extern int value;
    }
}

because you only want to declare index and value, not define them. (See this answer for the difference.)

sbi
Second one probably works because `extern "C"` doesn't mean anything for variables, so the `"C"` is ignored? Just a guess.
Oli Charlesworth
I think the more general question is why isn't the extern block setting extern for all.
linuxuser27
@linuxuser: It shouldn't. `extern "blah" T foo` just tells the linker which linking convention to follow for linking `foo`. `extern T bar`, OTOH, tells the compiler that it shouldn't worry about the existence of `bar`, the linker will (hopefully) find one somewhere. The latter would use (the implicit) "C++" linkage.
sbi
@Oli: I think it's common for C++ implementations to mangle a variable's type into its name, while it isn't for C. So `extern "C" int x` _does_ something: it requests the linker to use C's linkage to find `x`.
sbi
@sbi: I wasn't aware C++ mangled variable names. I will look into this...
Oli Charlesworth
@Oli it's pretty much required that the variable names are mangled. This doesn't have something to do with their types but primarily with their namepace. If they weren't mangled, how would `::index` and `Ada::index` be differentiated? The `extern "C"` means that they are not differentiated but declare actually the same object.
Johannes Schaub - litb
@Johannes: Yes, of course, namespaces!
Oli Charlesworth
+3  A: 

extern "C" only conveys the linking conventions to use for the code within the extern "C" block. Anything in that block will be linked against as if it were pure c. Confusingly, extern int is totally different. It means that you promise there is an actual int named index and an actual int named value somewhere, but they cannot be found here. In your implementation-A the ints are actually not extern in the second sense - the extern "C" only implies that they provide a strict c linking convention.

Same keyword but totally different uses, which is unfortunate since it leads to weird issues like this. Mixing them is legal (obviously), but they don't behave together the way that their name implies.

EDIT

See Charle's response for the true definition of the extern weirdness as defined in the C++ standard.

Peter
+1 Excellent explaination
linuxuser27
@Peter: I figured as much, but this still doesn't say why the second version of the code works.
sbi
+1 Thank you. This answers my question.
jerunh
@sbi I'm not sure Ive tested thoroughly enough to say that the second one works... Just that it links.
jerunh
I've read this answer a few times but I didn't understand the difference between (e.g.) `extern "C"` applied to a single declaration and `extern "C"` applied to a brace enclosed declaration list from it. Obviously I'm wrong (as it's been accepted), but I didn't see how it answered the original question.
Charles Bailey
@charles - Whoops, didn't mean not to address the implementation-B. I actually prefer your response since it sites the reference. Mine only attempted to answer why the first implementation doesn't work, but not why the second one does.
Peter
+3  A: 

A linkage-specifier (i.e. extern "C" or extern "C++") applied to a brace enclosed sequence of declarations has no effect on whether the enclosed declarations are definitions or not, however a linkage-specifier applied to a single declaration is treated as an extern specifier for the purposes of determining whether a declaration is also a definition. (7.5 para 7 of C++03)

So:

extern "C" { int a; } // a is declared and defined

extern "C" int a; // a is just a declaration (as if extern int a;)

extern "C" int a = 0; // a is a definition because of the initializer.
Charles Bailey