17

Can an extern "C" function accept or return C++-specific data types, such as references, pointers-to-members, or non-POD classes (by value)? I cannot find anything in the C++ standard that forbids this. Logically, I would expect the standard to say something about it, as the C ABI is not necessarily suitable for passing such types around.

The reason for me wanting to use C linkage has nothing to do with C compilers. The function is called only from C++ code. I just want to export unmangled function names from my dynamic libraries.

A silly code example:

class Foo {
  public:
    virtual void doit() = 0;
};

class Bar : public Foo {
  public:
    void doit() { std::cout << "Bar" << std::endl; }
};

extern "C" Foo& getFoo() { static Bar bar; return bar; }

extern "C" Bar getBar() { return Bar(); }

This compiles with GCC on Linux, and works as expected. Should it, standard-wise?

The question is a follow-up to a discussion in the comments to this question.

Update I have tested this with the Comeau compiler, it didn't complain.

Community
  • 1
  • 1
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243

4 Answers4

8

According to section 7.5.9 Linkage specifications (c++11 draft) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf

"Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved."

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • In my case the language of the caller and the language of the callee are called *C++* and *C++*, respectively. I would expect them to have pretty similar object layout strategies. – n. m. could be an AI Jan 20 '12 at 22:49
  • Indeed. btw, thanks for calling out my other (flawed) answer. – Johan Lundberg Jan 20 '12 at 22:53
  • As in Hans Passant's answer, you need to think about implementations. So while both *languages* are C++, if you use two different compilers (or even two different versions of the same compiler), there's no guarantee that it will work. – Daniel Rose Jan 20 '12 at 23:24
  • @n.m. It's easy to imagine a compiler/linker where it would not work. – Johan Lundberg Jan 20 '12 at 23:46
  • @Daniel It is well-known that C++ compilers do not mix even when using C++ linkage, so I'm obviously sticking to the same version throughout. – n. m. could be an AI Jan 21 '12 at 00:03
4

5.2.2 Function call [expr.call]

There are two kinds of function call: ordinary function call and member function62 (9.3) call. A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of expressions which constitute the arguments to the function. For an ordinary function call, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion (4.3) is suppressed on the postfix expression), or it shall have pointer to function type. Calling a function through an expression whose function type has a language linkage that is different from the language linkage of the function type of the called function’s definition is undefined (7.5).

So if the type of your function at the call point (ie the type of the function you are calling) is different from the type of the function at the definition point the result is undefined.

7.5 Linkage specifications (Paragraph 1) [dcl.link]

All function types, function names with external linkage, and variable names with external linkage have a language linkage.

From this we see that the language linkage is part of the type of the function. So both call site and call implementation must have exactly the same language linkage.

7.5 Linkage specifications (Paragraph 9) [dcl.link]

Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved.

Since C does not support C++ types, No C++ objects can be passed across the interface in a meaningful way. The function can not have C language linkage and be passed C++ objects. Thus we are breaking the type rules defined above.

But: Not only do we have to consider the layout of the object but how the object is passed/returned. Are values passed on the stack or by register this is all defined by the ABI and since C/C++ have different API there is no guarantee how the object is passed from one to the other or that the expectations of function cleanup are the same.

Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 1
    What is the basis for your reasoning in your second last paragraph? I don't see any reason that you can call a function with _C language linkage_ in C++ which either takes or returns a C++ object by value, reference or any other way. Obviously such a function would not be callable from C because C doesn't have such constructs but there should be no problem from C++. Language linkage is supposed to enable linking with other languages but it is only one part of the puzzle. You have to stick with an inteface that is common to both languages if that is what you want but that isn't the issue here. – CB Bailey Jan 20 '12 at 23:45
  • The call site and the function are written in C++ and see the same linkage ("C"). There's no problem of mixing linkages or languages. The only remaining point is the supposed inability of C linkage functions to pass around types that are "not C". I would reluctantly agree with that, but the standard doesn't say which types are "C enough" for this purpose. I expect any pointer to struct to be OK. But perhaps pointers that pass withing 6 feet from a virtual function are a big no-no? – n. m. could be an AI Jan 20 '12 at 23:52
  • @CharlesBailey: Because extern "C" also mean use the C ABI. Normally C++ applications are going to use a C++ ABI. If you manage to pass C++ objects across an extern "C" function you are just getting lucky that the C++ ABI aligns very closely to the C ABI for your particular compiler. Remember the C++ ABI was deliberately left undefined (unlike the C ABI which is defined for a platform) to allow compiler venders to optimize the call semantics (they are not required to use the stack for parameters (or whatever it is that the C ABI is bound too)). – Martin York Jan 21 '12 at 17:18
  • @n.m.: In my opinion the only safe thing to pass across the interface are pointers (and POD). Even if both sides are C++. Anything else you are asking for trouble. – Martin York Jan 21 '12 at 17:20
  • Neither the C standard nor the C++ standard say anything about an ABI at all, but the C++ standard defines rules for functions and variables declared `extern "C"` such as how they (effectively) ignore namespaces. While you have to refer to your implementations documentation if you are linking to an from objects defined in another lanaguage because "linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent", if you are only using C++ then you only have to worry about C++ rules. – CB Bailey Jan 21 '12 at 17:33
  • As far as I can tell - please correct me if I am wrong - there are no restrictions on the types of parameters or return types of functions declared `extern "C"` in C++. – CB Bailey Jan 21 '12 at 17:34
  • @CharlesBailey: I agree neither mention an ABI. The C++ ABI is deliberately' not defined (so C++ can optimize call semantics) but the C ABI is defined (thus allowing language linkage). But they both still exist for your compiler to work. The bit you quote above `**you have to refer to your implementations documentation**` is the point I am making. It may work because your C ABI and C++ ABI align closely enough that C++ object can pass over the interface but there is nothing to guarantee this (even if both sides are C++) as the linkage specifications define how those parameters are passed. – Martin York Jan 21 '12 at 17:45
  • @Charles Bailey: There is no restriction but `**you have to refer to your implementations documentation**` to see if (you will get lucky enough) these will pass over the C interface correctly. – Martin York Jan 21 '12 at 17:52
  • I don't agree with your interpretation of the standard. You only have to refer the the implementation specifics if you are actually talking between languages. If your program is entirely C++ then you can rely on the rules based on the standard (assuming a conforming implementation) and the standard guarantees that function calls work for functions with both C++ language linkage and C language linkage even if there is little point to using C++ language linkage in this case. – CB Bailey Jan 21 '12 at 19:36
  • @CharlesBailey: In my opinion you are talking between languages so it does apply. There is C++ -> C translation when crossing a C interface (these are not the same language). Thus no guarantee's (assuming a conforming implementation) with C++ objects. – Martin York Jan 21 '12 at 19:46
1

I don't have a definitive answer to this but extrapolating from Wikipedia, I'd say that this isn't guaranteed to work. The article (I don't have a copy of the standard) says extern "C" specs both the mangling and the ABI. C ABIs presumably don't spec non-C types so you're off in unspec'd behavior. (The article also cites cases where the C and C++ ABIs are different though that doesn't seem like it'd be an issue here.)

So while I imagine it might work in virtually all cases, I doubt you could cite chapter and verse from the standards that say it must. I'd not be terribly surprised to see a compiler that refused to compile it.

smparkes
  • 13,807
  • 4
  • 36
  • 61
  • I think the official name for "C type" in C++-land is "POD". But the article doesn't even mention the term, and neither does the section of the standard that talks about language linkage. – n. m. could be an AI Jan 20 '12 at 22:46
  • Right, but my point was that by definition, there's no C calling convention for references so you're in non-standard space. – smparkes Jan 20 '12 at 22:55
  • I would expect the standard to spell it out, but it doesn't. The problem with this is it is not clear which types are sufficiently C-like. Are all data pointers OK, or only pointers to POD, for example? Pointers to members? POD structs that contain pointers to members? Pointers to such structs? And so on. – n. m. could be an AI Jan 20 '12 at 23:19
  • All C types are going to work. Anything that's not expressible in plain C may not: the C++ standard explicitly says implementation-defined so you can't be sure every implementation will handle it. But, then, you may not be worried about every possible implementation. – smparkes Jan 20 '12 at 23:27
1

I just want to export unmangled function names from my dynamic libraries.

Have you considered using a .def file, which will allow you to export the decorated-named-functions by different names? This way you can enjoy the benefits of overloading, and at the same time give your functions "friendly" names as you like.

EXPORTS
funcOfInt=?func@a@@AAEXH@Z
funcOfSomethingElse=?func@a@@XYZ@W
Danra
  • 9,546
  • 5
  • 59
  • 117