2

I have an inline function, roughly like this:

inline void SomeFunction() {
    extern void SomeOtherFunction();
    SomeOtherFunction();
}

This is a simplification: my functions do have parameters and return values.

However, I want this header to work in both C and C++ files. Currently, linking fails because C++ files attempt to find an implementation of SomeOtherFunction with C++ linkage. I thought I could just fix this by using extern "C":

inline void SomeFunction() {
#ifdef __cplusplus
    extern "C" void SomeOtherFunction();
#else
    extern void SomeOtherFunction();
#endif
    SomeOtherFunction();
}

This causes Clang to fail with:

error: expected unqualified-id
    extern "C" void SomeOtherFunction();
           ^

How can I do this correctly?

melpomene
  • 84,125
  • 8
  • 85
  • 148
wham3
  • 51
  • 6
  • 2
    if your function depends on another that is only available at link time, it will never be inlined. – FBergo May 08 '18 at 19:34
  • What's the actual error message? – melpomene May 08 '18 at 19:35
  • @melpomene "Expected unqualified-id" is the complete error message (there is also the filename). – wham3 May 08 '18 at 19:38
  • 1
    @wham3 No, it's not. At minimum it's missing a filename and line number, but I suspect there's more that you left out. – melpomene May 08 '18 at 19:39
  • test.mm:3:12: error: expected unqualified-id extern "C" void SomeOtherFunction(); ^ – wham3 May 08 '18 at 19:43
  • See? You left out the part where it's pointing at the `"`. (Also you should edit that into your question, not hide it down here in the comments.) – melpomene May 08 '18 at 19:44
  • @fukanchik this is on a declaration – wham3 May 08 '18 at 19:47
  • @wham3 What exactly are you trying to achieve? Why would you want to do this? Just for fun? – BJovke May 08 '18 at 19:56
  • @wham3 You cannot decide in this module if linkage is "C" or not. That depends on how module where there's a definition of `SomeOtherFunction()` is compiled. – BJovke May 08 '18 at 19:57

4 Answers4

6

extern "C" is a linkage-specification. C++ standard section 7.5 Linkage specifications paragraph 4 states that:

A linkage-specification shall occur only in namespace scope (3.3).

E.g. you can say extern "C" in global namespace or some specific namespace. Outside of namespaces it is illegal.

Function declarations are possible though in smaller scopes. If you remove linkage specification your code will compile (but not link):

inline void SomeFunction() {
    extern void SomeOtherFunction();
    SomeOtherFunction();
}

If you really need SomeOtherFunction declaration in a smaller scope (e.g. hide from global scope) you can put your declaration into a header file to a dedicated scope and then use in your function:

Header:

namespace other {
    extern "C" void SomeOtherFunction();
}//namespace other

Code:

void SomeFunction()
{
    other::SomeOtherFunction();
}

Credit goes to these two answers on stackoverflow: here and here.

fukanchik
  • 2,811
  • 24
  • 29
4

From the C++11 standard (in [dcl.link], emphasis mine):

4 Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope.

(linkage-specification refers to extern string-literal ..., i.e. extern "C" in your case.)

This means you can't have extern "C" inside of a class or function.

What's the point of declaring SomeOtherFunction inside of SomeFunction? It still has to be a global symbol and visible to the linker.

So why not do this?

#ifdef __cplusplus
    extern "C" 
#endif
void SomeOtherFunction();

inline void SomeFunction() {
    SomeOtherFunction();
}

The following also seems to work:

extern "C" {
    inline void SomeFunction() {
        extern void SomeOtherFunction();
        SomeOtherFunction();
    }    
}

But it would have the side effect of making SomeFunction also use C linkage (which is hopefully OK as (per your requirements) it needs to be usable from C, too).

R Sahu
  • 204,454
  • 14
  • 159
  • 270
melpomene
  • 84,125
  • 8
  • 85
  • 148
  • While this is a sound solution to the problem, it does not answer the question of why is `extern "C" ...` not legal inside a function. – R Sahu May 08 '18 at 19:50
  • I guess that if you declare the function inside the function kind of C++'s "private" equivalent. Linker will find it, but if you writing a library it won't be visible to the user of your library. I guess that's the OP's point. – zupazt3 May 08 '18 at 19:52
  • 1
    @melpomene, I agree that that's not the OP's question. It's a question of the curious mind :) – R Sahu May 08 '18 at 19:53
  • @melpomene You see that it doesn't work, doesn't it? And what's the point of doing this at all? – BJovke May 08 '18 at 19:55
  • @melpomene `extern "C"` is not inside the function as you claimed it is possible. The second example is also not, but you're making `SomeFunction()` compile with C linkage, which is not stated anywhere in the question. – BJovke May 08 '18 at 19:59
  • 1
    @BJovke I never claimed `extern "C"` was possible inside a function. Your answer claimed `extern` (as in any declaration of an external symbol) wasn't allowed inside of a function, which is not true: "*Only anonymous functions (lambdas and function objects) can be declared inside blocks.*" And I know my second example makes `SomeFunction` have C linkage; my answer explicitly says so. – melpomene May 08 '18 at 20:06
  • @melpomene Well yes, you're right. But the question has interconnected mixture of both, which can't work. And I don't see the point in any of this anyway. – BJovke May 08 '18 at 20:12
  • @RSahu I added a reference to the standard explaining the error. – melpomene May 08 '18 at 20:12
1

You can do this like that. I assume you have a header file, a C source and a C++ source.

Header file:

inline void SomeFunction()
{
    void SomeOtherFunction_Bridge();
    SomeOtherFunction_Bridge();
}

C++ source:

extern "C" 
{
    void SomeOtherFunction();
}

void SomeOtherFunction_Bridge()
{
    SomeOtherFunction();
}

C source:

void SomeOtherFunction()
{
    // Function with a code doing something
}

void SomeOtherFunction_Bridge()
{
    SomeOtherFunction();
}

Checked on GCC, it compiles.

zupazt3
  • 966
  • 9
  • 30
-3

extern tells the compiler that a symbol (function, variable) is defined in some other module. When this module (C++ file) is compiled, object file contains a list of external symbols it needs. So this is on a level of a C++ file, it cannot be in smaller scope (function, block).

BJovke
  • 1,737
  • 15
  • 18
  • 2
    That's simply wrong. You can have function declarations in blocks. – melpomene May 08 '18 at 19:38
  • @melpomene No. Only anonymous functions (lambdas and function objects) can be declared inside blocks. Give me compilable example that shows otherwise. – BJovke May 08 '18 at 19:42
  • @BJoke try this: `void foo(){extern bar();}` – fukanchik May 08 '18 at 19:44
  • @melpomene That's not the point of the question. `extern "C"` and `extern` usage depend on the module where the symbol is defined, not on the module which is using it. So if the module where symbol is defined is compiled with C linkage you must use the same thing all around your module which is using it. It cannot be sometimes "C" and sometimes not. – BJovke May 08 '18 at 19:50
  • @BJovke Sorry, you've lost me there. You can have symbols with C linkage and symbols with C++ linkage, all defined in the same module. Similarly, another module can reference and use symbols with C linkage and symbols with C++ linkage. I don't understand what you're trying to say here. – melpomene May 08 '18 at 19:58