3

In a file, I have this macro:

#if defined(__cplusplus)
    #define EXTERN_C extern "C"
#else
    #define EXTERN_C extern
#endif

But searching on the web, it doesn't seem typical for such macros to have an extern on the C path (though some examples exist). They generally come in pairs more akin to EXTERN_C_BEGIN and EXTERN_C_END to wrap up a group of declarations...and are a no-op in C builds.

But this means in a header file, you wind up having to write:

EXTERN_C_BEGIN
extern int x; /* ...but I already said extern... */
void doSomething(int y);
EXTERN_C_END

This happens because extern "C" int x; is equivalent to extern "C" { extern int x; }... not extern "C" { int x; }. The latter form only gives "C linkage", but not the historical meaning of C extern. Then if you try to make the single-line form decay in C to extern, you can't say EXTERN_C extern int x;, the compiler will complain in the C++ build at the two-externs-on-one-declaration.

Since there's no begin/end form, it's (often) going to mean more typing. But I prefer the "announcement of intent" to the compiler, even in the C case:

EXTERN_C int x;
EXTERN_C void doSomething(int y); /* does `extern` help the C case? */

I cannot offhand seem to create a good scenario where having the extern on the function declarations helps catch a mistake in C. But intuitively it seems there might be value in the redundancy, and as I say, having to do extern "C" and then later still put extern on things feels awkward.

What is the reasoning to prefer (or avoid) either approach?

  • 1
    You normally have a trio of macros, such as `EXTERN_C`, `EXTERN_C_BEGIN`, `EXTERN_C_END`, where `EXTERN_C` is a prefix for a single declaration (`EXTERN_C void function(void);`, whereas you use `EXTERN_C_BEGIN` before and `EXTERN_C_END` after a group of declarations. You can use an empty replacement for `EXTERN_C` in C because function declarations are `extern` by default anyway. You wouldn't use `extern` in the C code for `EXTERN_C_BEGIN`; it could easily introduce bugs. – Jonathan Leffler Oct 31 '17 at 02:54
  • @JonathanLeffler I've not seen the trio. But for variable declarations using the EXTERN_C if it left out the `extern` in the C build you'd have to say `EXTERN_C extern int x;`, which expands to illegal C++ *(though note `extern "C" {extern int x;}` is actually legal)*. I guess I am just wondering why someone who controls their header sources wouldn't switch to an all-EXTERN_C, even if it meant touching each declaration...which raises questions of if there's any value to being explicit about function declarations being `extern`. – HostileFork says dont trust SE Oct 31 '17 at 03:04
  • AFAIK, there is no C++ linkage for variables — variable names are not mangled. (I checked that recently, in the last couple of months, but I should probably recheck.) That means you don't need to write `EXTERN_C extern int x;` — it probably doesn't fail, but it isn't necessary. And the forms `#define EXTERN_C_BEGIN extern "C" {` and `#define EXTERN_C_END }` are needed to wrap a header, for example: `EXTERN_C_BEGIN` / `#include "c-header.h"` / `EXTERN_C_END` on three lines of source. – Jonathan Leffler Oct 31 '17 at 03:08
  • I'm also confused by your comment "but I already said extern...". `extern "C"` and `extern` are different things that happen to share a keyword. `extern "C" { int x; }` is not the same as `extern "C" { extern int x; }` – M.M Oct 31 '17 at 03:13
  • @M.M *"`extern "C"` and `extern` are different things that happen to share a keyword.* Perhaps, but there's really only one intention in usage of this macro: "I'm writing definitions that can compile under C or C++, this declaration is external, and it should have C linkage." If the nuances of the language can be collapsed into a helpful macro that does the right thing, that seems like a worthwhile goal. If it can't be done, the understanding of why not is illustrative. – HostileFork says dont trust SE Oct 31 '17 at 03:22
  • @M.M Fair enough. I definitely feel that when there's a large body of code using EXTERN_C out there that does not decay to `extern` for the C case, or includes the brace or doesn't, that there's enough variance in practice to be skeptical. Would you mind forming this as an answer? *(It seems if anything, EXTERN_C_BEGIN and EXTERN_C_END can at least not really mean much of anything else...)* – HostileFork says dont trust SE Oct 31 '17 at 03:38
  • @JonathanLeffler Cppreference [special rules for "C" linkage](http://en.cppreference.com/w/cpp/language/language_linkage) seems to imply that name mangling for variables comes into play when you're dealing with namespaces, and that C variables and functions are considered in their own dedicated namespace.. – HostileFork says dont trust SE Oct 31 '17 at 03:47
  • My experimentation didn't run to namespaces, anonymous or named. They're pretty inimical to C linkage. If `extern "C"` on a variable inside a named namespace avoids name mangling on variables, it makes sense, though it doesn't make the code easier to understand. Ordinary global namespace variables are not mangled in C++, AFAIK. – Jonathan Leffler Oct 31 '17 at 03:53
  • @JonathanLeffler It looks like people say that to be standard [you need it on global variables](https://stackoverflow.com/a/27939238/211160), which I feel echoes cppreference technical details. But I can't find a way to say `extern "C"` as well as `extern` on the same definition unless the **extern "C" { extern ... }** form is used. If I'm correct, that suggests one might lean to favoring the _BEGIN and _END form solely for regularity, vs. having a "broken" member of the "trio" (?) – HostileFork says dont trust SE Oct 31 '17 at 04:15

3 Answers3

3

Usually you see this in headers that were written for C and later made compatible with C++. The easiest way to do that is to simply wrap the whole header in extern "C" { and }. That way there's no need to touch every single declaration, and the main contents can stay plain C.

The macros you're talking about simplify this wrapping. Now it's just two lines:

EXTERN_C_BEGIN
EXTERN_C_END

Instead of the slightly more elaborate dance you'd have to do otherwise to hide the lines from the C compiler:

#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif

The other issue is that global variables are relatively rare (compared to functions), and you don't need extern in function declarations. So there's less need for an extern macro.

melpomene
  • 84,125
  • 8
  • 85
  • 148
  • @M.M I was talking about this case: "*They generally come in pairs like EXTERN_C and EXTERN_C_END to wrap up a group of declarations*" (the linked post also has a `{` in the definition). OP is contrasting this with their own macro, which doesn't have `{`. – melpomene Oct 31 '17 at 03:10
  • OK, I see what he's getting at now – M.M Oct 31 '17 at 03:11
1

If there are multiple declarations to be marked then your way ends up being more verbose than the begin/end way.

My most preferred form is the one where nothing is hidden -- someone reading the code doesn't have to go and look up what EXTERN_C is defined as in order to understand the code. With your suggestion, the reader would have to go and check whether it was extern or blank in C mode, or even something else.

Your way would work but I think a large part of it is conforming to what others reading the code will expect.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • While my way [has historical precedent in Microsoft code](https://github.com/tpn/winsdk-10/blob/ab507ba07037c452c4ca2eb2f99dd21370a8aa54/Include/10.0.10240.0/shared/basetyps.h#L18), I note that `extern "C" int x;` as a declaration on its own line and `extern "C" { int x; }` are not the same. The one line form takes on the meaning of `extern "C" { extern int x; }`. So if you try to make the single-line form decay in C to extern, you can't say `EXTERN_C extern int x;` ...the C++ build won't allow it the doubling up of externs. Doesn't mix well with the _BEGIN and _END form, in any case. – HostileFork says dont trust SE Oct 31 '17 at 07:13
0

Adding 'extern' to a function declaration in a header file have no real benefits. So, the 'extern' is present in C++ versions of the macro because it is needed to tell the compiler to use C calling-convention for such functions. For a C compiler the extern can be ommited altogether.

See this answer for my details: https://stackoverflow.com/a/856736/1050181

Paulo Facó
  • 397
  • 1
  • 4
  • 9