-->

Interface to C++ objects through extern “C” functi

2019-06-15 07:25发布

问题:

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.

回答1:

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."



回答2:

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.



回答3:

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.



回答4:

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