I observed a function in a dll which has C linkage. This function returns class type. I am not sure how this is made possible as C doesn't understand class.
I wrote a sample dll and program myself and noted that the VC++ compiler shows a warning to this effect but doesn't stop you. The program is able to GetProcAddress of this function and call it to receive the returned object. The class definition was made available to program.
Furthermore, if I write a function with C linkage that returns a class type where this class is not even exported, the compiler doesn't issue any warning. The program can consume this function from the dll provided the class definition is made available to it.
Any thoughts on how this works? Is such a behavior compiler/platform specific?
You are misunderstanding the behavior of extern "C"
.
It only impacts the name of the function in the object file by preventing name mangling. It does not make a function neither "more C" or "less C++". The only additional limitation which extern "C"
adds to a C++ function is that is shall not be overloaded.
It's possible for functions with C linkage to return objects that can't be expressed in C so long as they can be manipulated in C. According to my copy of Design & Evolution of C++ (section 11.3.3):
We considered several alternatives to the type-safe linkage schemes before deciding on the one actually added to the language [Stroustrup,1988]: ...
- provide type-safe linkage only for functions that couldn't be C functions because they had types that couldn't be expressed in C. ...
A function declared to have C linkage still has C++ calling semantics. That is, the formal arguments must be declared, and the actual arguments must match under the C++ matching and ambiguity control rules. ... Had we provided special services for C, we would have been obliged to add an unbounded set of language calling conventions to C++ compilers [for linking to Pascal, Fortran, PL/I, etc.]. ...
Linkage, inter-language calls, and inter-language object passing are inherently difficult problems and have many implementation-dependent aspects. ... I expect we haven't heard the last of this matter.
That is, C++ linkage isn't based on whether the types involved are valid C types. That is an intentional design decision. It allows you to create a DLL compiled in C++ but that can be used from C via a header:
// in the header
struct Foo; // forward declaration
#ifdef __cplusplus
extern "C" {
#endif
struct Foo* create_foo();
void destroy_foo(struct Foo*);
void foo_bar(struct Foo*);
#ifdef __cplusplus
} // extern "C"
// now declare Foo
struct Foo {
void bar();
};
#endif
// in the implementation file
#include <iostream>
extern "C" {
Foo* create_foo()
{
return new Foo();
}
void destroy_foo(Foo* f)
{
delete f;
}
void foo_bar(Foo* f)
{
f->bar();
}
}
void Foo::bar()
{
std::cout << "Foo::bar() called\n";
}
Note the calls to new
, delete
and std::cout
. These all require the C++ runtime. Therefore, the implementation file must be compiled with C++, but since the functions have C linkage, the header can be used from either C or C++.
So how do you get a Foo
in C? In this case, you don't, because there's no way to fully declare it in the header in a way that C will understand. Instead, C only sees the forward declaration, which creates an incomplete type. C doesn't know how large an incomplete type is, so C functions can't create it or operate on them directly, but C does know how large a pointer to an incomplete type is, so C can operate on a pointer to an incomplete type. You can get much more creative and actually create POD types in C++ that you can operate on directly in C.
As long as you only use Foo*
s in C, you don't have to actually define the Foo
struct in C. It so happens that APR uses a similar design ("Creating an APR Type," I couldn't find a better link).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With